Nuevos Patrones de Diseo Introduccin Abstract Factory Data Access Object (DAO) DAO con Abstract Factory Conclusiones Finales Ejercicios Nuevos Patrones de Diseo 2 Introduccin Hasta el momento, los programas que escribamos manejaban sus datos en memoria o bien en un nico mecanismo de almacenamiento (persistencia) en disco (por ejemplo, una estructura de archivos en disco o un manejador de bases de datos concreto). Con este esquema, si en el futuro queremos migrar de un mecanismo de persistencia a otro, vamos a tener que modificar el cdigo de las clases existentes a efectos de adaptarnos al nuevo mecanismo. Esto implica tener que recompilar las clases luego de modificadas y probarlas nuevamente, sin mencionar adems que perdemos el mecanismo de almacenamiento anterior. Por ejemplo, si actualmente manejamos colecciones de objetos en memoria y maana queremos guardar dichos objetos en una base de datos, vamos a tener que cambiar la implementacin de dichas colecciones de modo tal que en vez de almacenar los objetos en estructuras como Vector o Hashtable, lo hagan en tablas de una BD. 2 Nuevos Patrones de Diseo 3 Introduccin Nos gustara poder migrar de un mecanismo de persistencia a otro sin tener que modificar cdigo existente. De esta manera no tendremos que recompilar las clases ya existentes, no tendremos que testearlas nuevamente, y adems habremos ganado la libertad de elegir qu mecanismo de almacenamiento queremos utilizar (el anterior,el nuevo o cualquier otro que se nos ocurra en el futuro). Para lograr lo anterior, vamos a estudiar dos nuevos Patrones de Diseo llamados Abstract Factory y Data Access Object (DAO) los cuales, al ser usados en forma combinada, permiten lograr lo anterior. Es importante aclarar que estos dos patrones son independientes entre s (de hecho, resuelven problemas bien diferentes) e inicialmente vamos a estudiarlos por separado. Sobre el final del captulo veremos cmo combinarlos a efectos de lograr nuestro propsito. El resultado ser un sistema configurable a diferentes mecanismos de persistencia, permitiendo migrar a un nuevo mecanismo sin necesidad de re-escribir o modificar cdigo, sino incorporando nuevas clases. Nuevos Patrones de Diseo 4 Abstract Factory Problema Problema: : Se quiere proveer una interfaz para crear familias de productos relacionados sin especificar sus clases concretas. Soluci Soluci n n: : Definir tantas jerarquas de interfases como tipos de productos existan. Cada clase de cada jerarqua representa a un producto perteneciente a una familia concreta. Definir una jerarqua de interfases adicional (las fbricas), cuyas clases permitan instanciar los distintos tipos de productos pertenecientes a las distintas familias de productos. Cada fbrica tendr tantos mtodos de instanciacin como tipos de productos existan. 3 Nuevos Patrones de Diseo 5 Abstract Factory Soluci Soluci n n: : Nuevos Patrones de Diseo 6 Abstract Factory Soluci Soluci n n: : 4 Nuevos Patrones de Diseo 7 Abstract Factory Soluci Soluci n n: : Nuevos Patrones de Diseo 8 Abstract Factory public interface FabricaAbstracta { public ProductoA crearProductoA(); public ProductoB crearProductoB(); } public class FabricaConcreta1 implements FabricaAbstracta { public ProductoA crearProductoA() { return new ProductoA1(); } public ProductoB crearProductoB(); { return new ProductoB1(); } } public class FabricaConcreta2 implements FabricaAbstracta // idem anterior, pero retorna productos A2 y B2. 5 Nuevos Patrones de Diseo 9 Abstract Factory Consecuencias del Patr Consecuencias del Patr n n: : Los clientes que usan los productos no necesitan conocer las implementaciones concretas de los mismos. Simplemente se manejan con los mtodos provistos por las interfases de dichos productos. La incorporacin de nuevas familias de productos es muy fcil de realizar. Basta con incorporar una nueva fbrica y una nueva clase que implemente la interfaz de cada tipo de producto. No se modifica cdigo. Agregar nuevos tipos de productos es difcil (hay que agregar una nueva jerarqua de productos y agregar un mtodo nuevo en todas las fbricas definidas). Esto implica definir una nueva interfaz de productos y adems agregar un nuevo cabezal a todas las fbricas, teniendo que modificar el cdigo de las mismas. Este patrn es particularmente apropiado cuando lo que interesa es incorporar nuevas familias de productos ms que nuevos tipos. Nuevos Patrones de Diseo 10 Abstract Factory Ejemplo Ejemplo: : Se desea desarrollar una interfaz grfica configurable para un determinado sistema. Concretamente, se desean tener temas de visualizacin de modo tal que slo con cambiar el tema se logre cambiar el aspecto de los botones, ventanas y campos de texto. Inicialmente se contar con dos temas pero se prev la incorporacin paulatina de nuevos temas de visualizacin. El primer tema se llamar Naturaly tendr botones verdes, campos de texto blancos y ventanas rectangulares. El segundo tema se llamar Espacialy tendr botones negros, campos de texto anaranjados y ventanas hexagonales. Los tres tipos de productos sern las Ventanas, Botones y Campos de Texto, mientras que las dos familias de productos sern la familia natural y la familia espacial. 6 Nuevos Patrones de Diseo 11 Abstract Factory Nuevos Patrones de Diseo 12 Abstract Factory String tipoFabrica; FabricaTema fab; Ventana ven; Botn bot; CampoTexto cmpTxt; // cargo el tipo de fbrica desde un archivo de texto ... // instancio din din micamente micamente el tipo de fbrica fab = Class.forName(tipoFabrica).newInstance(); ven = fab.crearVentana() bot = fab.crearBoton() cmpTxt = fab.crearCampoTexto() ven.add(bot) ven.add(cmpTxt) 7 Nuevos Patrones de Diseo 13 Abstract Factory El tipo de la fbrica es un string conteniendo el nombre de la clase que debemos instanciar. En este ejemplo la estamos instanciando en forma dinmica. Recordemos que en J ava es posible instanciar clases en forma dinmica mediante Class.forName. Esto significa que no necesitamos nombrar explcitamente la clase que queremos instanciar, sino que cargamos su nombre en un string y luego se lo pasamos a Class.forName quien nos devuelve una instancia de la clase con ese nombre. Observemos que, en nuestro ejemplo, dicha clase ser o bien FabricaNaturalo bien FabricaEspacialpero en el cdigo no necesitamos nombrarla explcitamente, gracias al polimorfismo. Simplemente nos referimos a la interfaz base FabricaTema. La variable fabreferenciar a una fbrica u otra, sin importar cul sea. De hecho, en ninguna parte del cdigo anterior podemos deducir con cual fbrica estamos trabajando ni con qu familia de productos, lo cual es justamente el objetivo de este patrn de diseo. Nuevos Patrones de Diseo 14 Data Access Object (DAO) Problema Problema: : Nuestro sistema necesita almacenar datos persistentemente en algn mecanismo de almacenamiento, como ser: archivos planos, archivos XML, bases de datos, sistemas legados, etc. Soluci Soluci n n: : Para cada objeto de la capa lgica que interese persistir, crear una clase denominada Data Access Object (DAO) que tenga los mtodos necesarios para acceder a la persistencia del objeto. <<persistencia>> Data Access Object +mtodo1 () +mtodo2 () <<lgica>> Objeto persiste a 8 Nuevos Patrones de Diseo 15 Data Access Object (DAO) Ejemplo Ejemplo: : <<collection>> COLPACIENTES - tabla: Hashtable +agregar (pac: Paciente) +asignarConsulta (doc:int,co:Consulta) +listarPacientes ( ) : DataPacientes[ ] PACIENTE - doc: integer - nom, ape: String - colC: ColConsultas +Paciente (doc, nom, ape) +getDocumento ( ) : integer +asignarConsulta (co:Consulta) +getDataPaciente ( ): DataPaciente <<collection>> COLCONSULTAS - lista: List +asignarConsulta (co:Consulta) +cantidadConsultas ( ): integer - fecha: Date - hora: Time +Consulta (fec,hora) +getFecha (): Date +getHora (): Time CONSULTA * * 1 1 1 1 Nuevos Patrones de Diseo 16 Data Access Object (DAO) El diagrama de clases anterior result de aplicar los refinamientos de diseo tradicionales a un diagrama de clases que originalmente era conceptual. En dicho diagrama, los objetos de la capa Lgica (Pacientes y Consultas) son almacenados en colecciones de objetos en memoria. Ahora se desea migrar el mecanismo de almacenamiento de tales objetos hacia una base de datos con el siguiente esquema de tablas: Pacientes (docPac, nom, ape) Consultas (fecha, hora, docPac) Vamos a aplicar el patrn DAO de forma tal que adaptemos nuestro diseo para dicho mecanismo de persistencia. 9 Nuevos Patrones de Diseo 17 Data Access Object (DAO) <<persistencia>> DAO PACIENTES +agregar (pac: Paciente) +asignarConsulta (doc:int,co:Consulta) +listarPacientes ( ) : ColDataPacientes PACIENTE - doc: integer - nom, ape: String - daoC: DAOConsultas +Paciente (doc, nom, ape) +getDocumento ( ) : integer +asignarConsulta (co:Consulta) +getDataPaciente ( ): DataPaciente <<persistencIa>> DAO CONSULTAS - docPac: integer +asignarConsulta (co:Consulta) +cantidadConsultas ( ): integer - fecha: Date - hora: Time +Consulta (fec,hora) +getFecha (): Date +getHora (): Time CONSULTA * * 1 1 1 1 Nuevos Patrones de Diseo 18 Data Access Object (DAO) Las clases que antes eran colecciones de objetos fueron sustituidas por clases DAO. Las multiplicidades que antes existan as como las relaciones y los mtodos que existan se siguen manteniendo en la nueva versin. Las colecciones anteriores guardaban sus objetos en memoria (la de pacientes lo haca en un Hash, mientras que la de Consultas lo haca en un List). Las nuevas colecciones(los DAO) en lugar de guardar sus objetos en memoria, lo harn en las tablas de la base de datos. Ntese nuevamente que los cabezales de los mtodos no cambiaron. Lo que s cambia es la implementacin interna de dichos mtodos (antes accedan a memoria y ahora lo harn a tablas de la BD). El nico atributo que se modific es el atributo docPacdel DAO de Consultas. Esto es porque necesitamos vincular en la BD a cada consulta con el paciente que le corresponde. Cuando trabajbamos en memoria esto no era necesario porque cada paciente contaba con su propia coleccin de consultas en memoria. 10 Nuevos Patrones de Diseo 19 Data Access Object (DAO) En este ejemplo vamos a introducir una nueva clase que represente la comunicacin con la base de datos. Esta clase ser Singleton y ser utilizada por los dos Data Access Objects definidos en el diagrama anterior. - urlDBMS: String - nombreBase: String - instance: AccesoBD - AccesoBD( ) +getInstance ( ): AccesoBD + prepareStmt (params) + execQuery (query:String): ResultSet + execUpdate (query: String) <<persistencia / Singleton >> ACCESO BD Nuevos Patrones de Diseo 20 Data Access Object (DAO) Considere el siguiente diagrama de secuencia para el caso de uso Asignar Consulta a Paciente: colP:ColPacientes pac:Paciente asignarConsulta(doc,co) pac =find (doc) asignarConsulta (co) asignarConsulta (co) colC:ColConsultas 11 Nuevos Patrones de Diseo 21 Data Access Object (DAO) Mtodo asignarConsulta de la clase ColPacientes (en memoria) public void asignarConsulta (Integer doc, Consulta co){ Paciente pac; pac = tabla.find (doc); if (pac == null) throw PacienteNoExisteException; else{ pac.asignarConsulta(co); } } Esta es la implementacin del mtodo asignarConsulta cuando los pacientes eran almacenados en la tabla de hash dentro de la coleccin de pacientes. Ahora vamos a modificar su implementacin, considerando que ahora los pacientes se almacenan en la base de datos. Nuevos Patrones de Diseo 22 Data Access Object (DAO) Mtodo asignarConsulta de la clase DAOPacientes (en la BD) public void asignarConsulta (Integer doc, Consulta co){ Paciente pac; AccesoBD con = AccesoBD.getInstance(); con.prepareStmt(doc); ResulSet rs = con.execQuery (Select * FROM Pacientes where doc = (?)); if (rs == null) throw PacienteNoExisteException; else{ pac = new Paciente (rs.doc,rs.nom,rs.ape); pac.asignarConsulta(co); } } 12 Nuevos Patrones de Diseo 23 Data Access Object (DAO) En azul se marcaron las modificaciones que hubo que hacerle al mtodo entre la primera versin (coleccin de pacientes en memoria) y la segunda versin (pacientes almacenados en la BD). En la primera versin, buscbamos al paciente en la tabla de hash y una vez encontrado, le asignbamos la nueva consulta. En la segunda versin, al paciente tuvimos que ir a buscarlo en la base de datos, por eso sustituimos el acceso al hash por una consulta sobre la tabla de pacientes. En la primera versin, la tabla de hash ya nos devolvi al paciente instanciado, pero en la segunda versin sus datos vinieron sueltos dentro del ResultSet. Por ello es que primeramente tuvimos que instanciarlo a partir de esos datos para recin luego asignarle su nueva consulta. Ntese que la lgica de fondo en la resolucin del caso de uso sigui siendo la misma luego de las modificaciones. Lo nico que cambiamos fue el lugar de almacenamiento donde fuimos a buscar al paciente. Nuevos Patrones de Diseo 24 Data Access Object (DAO) Mtodo asignarConsulta de la clase Paciente // versin sin sin DAO (col de consultas en memoria) public void asignarConsulta (Consulta co){ colC.asignarConsulta(co) } // versin con con DAO (dao de consultas en BD) public void asignarConsulta (Consulta co){ daoC.asignarConsulta(co) } 13 Nuevos Patrones de Diseo 25 Data Access Object (DAO) La implementacin del mtodo asignarConsulta de la clase Paciente se mantuvo incambiada en ambos casos. Esto se debe a que el paciente no guarda directamente sus consultas, sino que delega dicha tarea a su coleccin (o DAO) de consultas, quien encapsula la forma de almacenamiento, logrando as que el paciente se desentienda del mecanismo concreto por el cual sus consultas son almacenadas (principios de bajo acoplamiento y alta cohesin entre las clases). En la primera versin, el paciente le pide a su coleccin de consultas en memoria que almacene la consulta, mientras que en la segunda versin se lo pide a su DAO de consultas (quien, internamente, accede a la BD en el disco). La forma de hacer dicho pedido no cambi porque el paciente invoc al mtodo de la coleccin de consultas (o del DAO, segn la versin) y recordemos que habamos mantenido los mismos cabezales tanto en la coleccin de consultas en memoria como en el DAO de consultas. Nuevos Patrones de Diseo 26 Data Access Object (DAO) Mtodo asignarConsulta de la clase ColConsultas public void asignarConsulta (Consulta co){ lista.addElement(co); } Mtodo asignarConsulta de la clase DAOConsultas public void asignarConsulta (Consulta co){ Date fec = co.getFecha(); Time hor = co.getHora(); AccesoBD con = AccesoBD.getInstance(); con.prepareStmt(fec,hor,docPac); con.execUpdate(Insert into Consultas values (?,?,?)); } 14 Nuevos Patrones de Diseo 27 Data Access Object (DAO) En verde se marcaron las modificaciones que hubo que hacerle al mtodo entre la primera versin (coleccin de consultas en memoria) y la segunda versin (consultas almacenadas en la BD). En la primera versin, insertbamos la nueva consulta al final de la Lista en memoria usada por la clase Coleccin de Consultas. En la segunda versin, tuvimos que insertar la nueva consulta en la base de datos, por eso sustituimos el acceso al list por una consulta sobre la tabla de consultas. En la primera versin no hizo falta vincular la consulta con el paciente, pues cada paciente tena su propia coleccin de consultas. En la nueva versin, tuvimos que usar el documento del paciente (atributo del DAO de consultas) para vincularlo con la nueva consulta en la tabla consultas de la base de datos. Nuevamente, la lgica de fondo en la resolucin del caso de uso sigui siendo la misma luego de las modificaciones. Lo nico que cambiamos fue el lugar de almacenamiento de la nueva consulta. Nuevos Patrones de Diseo 28 Data Access Object (DAO) Observaciones del Ejemplo anterior: En un entorno concurrente, mantener una nica conexin con la base de datos (como hicimos en el ejemplo) puede perjudicar notoriamente la performance del sistema. En tal caso, es preferible manejar un pool de transacciones a la base de datos a efectos de permitir el acceso concurrente, en lugar de una nica conexin. Volveremos sobre esto ms adelante cuando estudiemos transaccionalidad sobre bases de datos. El patrn de diseo DAO respeta muy bien los principios de bajo acoplamiento y alta cohesin que rigieron todos nuestros diseos OO, logrando as una excelente distribucin de responsabilidades. Como contrapartida, la performance a nivel de la BD puede verse enlentecida (consultas redudantesy/o poco aprovechamiento de la potencia del motor de BD) en cuyo caso quizs convenga optar por un diseo ms orientado a bases de datos que orientado a objetos. 15 Nuevos Patrones de Diseo 29 Data Access Object (DAO) Consecuencias del Patrn: El mecanismo concreto de almacenamiento queda encapsulado en forma interna a las clases DAO definidas, logrando as una adecuada separacin entre la capa lgica y la capa de persistencia. No obstante, para migrar de un mecanismo de persistencia a otro, debemos modificar el cdigo de las clases DAO. Sera deseable tener la posibilidad de poder cambiar de persistencia con facilidad y sin tener que modificar el cdigo de las clases DAO. Para ello, lo que haremos es combinar el patrn DAO con el patrn Abstract Factory de modo tal que los productos de las fbricas sean distintos tipos de DAOs que accedan a distintos tipos de persistencia (por ejemplo, una estructura de archivos en FILE SYSTEM y un motor de BD, o bien dos motores distintos de BD). Ahora vamos a combinar DAO con Ahora vamos a combinar DAO con Abstract Abstract Factory Factory Nuevos Patrones de Diseo 30 DAO con Abstract Factory <<interface>> Factory Persistencia + crearDAO ( ): DAO FactoryA +crearDAO ( ): DAO FactoryB +crearDAO ( ): DAO <<interface>> IDAO + mtodo1 () + mtodo2 () <<lgica>> Objeto <<persistencia>> DAO Tipo A +mtodo1 () +mtodo2 () <<persistencia>> DAO Tipo B +mtodo1 () +mtodo2 () 16 Nuevos Patrones de Diseo 31 Data Access Object (DAO) Ahora los mtodos del DAO se definen en una interfaz en lugar de definirlos directamente en la clase DAO. Esta interfaz representa un tipo de producto del patrn Abstract Factory que vimos anteriormente. Si deseamos tener ms de un mecanismo de persistencia, vamos a crear ms de una clase que implemente los mtodos de la interfaz DAO. Cada clase acceder a un mecanismo de persistencia concreto y pertenecer a una familia de productos del patrn Abstract Factory. Si en el futuro queremos agregar un nuevo mecanismo de persistencia, no tenemos que modificar el cdigo de las clases DAO existentes, sino que simplemente agregaremos una nueva clase que implemente los mtodos de la interfaz del DAO. De acuerdo con el patrn Abstract Factory, definimos tambin una interfaz que represente a la fbrica y tantas fbricas concretas como mecanismos de persistencia vayamos a tener. De esta manera, hemos combinado DAO con Abstract Factorypara lograr el propsito planteado al inicio de este captulo. Nuevos Patrones de Diseo 32 DAO con Abstract Factory Ejemplo Modificado Ejemplo Modificado: : <<interface>> Factory Persistencia + crearDAOPacientes () + crearDAOConsultas () Factory BD +crearDAOPacientes() +crearDAOConsultas() <<interface>> IDAOPacientes <<lgica>> PACIENTE <<persistencia>> DAOPacientes <<interface>> IDAOConsultas <<persistencia>> DAOConsultas * <<lgica>> CONSULTA * 1 1 1 1 17 Nuevos Patrones de Diseo 33 Data Access Object (DAO) Las clases DAOPacientes, DAOConsultas, Paciente y Consulta son las mismas que antes tenamos en la primera versin del ejemplo, mantienen los mismos mtodos y se comportan exactamente igual. Hemos incorporado las interfases IDAOPacientes e IDAOConsultas, las cuales definen los cabezales de los mtodos de las clases DAO. Tambin hemos incorporado la jerarqua de clases de las fbricas del patrn Abstract Factory. En este ejemplo modificado tenemos dos tipos de productos (DAO de pacientes y DAO de consultas) y una sola familia de productos (la del nico mecanismo de persistencia que tenemos por ahora). Si en el futuro queremos incorporar un nuevo mecanismo de persistencia posible no tendremos que modificar cdigo y no perderemos la posibilidad de usar el mecanismo actual. Lo que haremos ser incorporar una nueva familia de productos (ie: una nueva fbrica, una nueva clase que implemente la interfaz DAO pacientes y una nueva clase que implemente la interfaz DAO consultas). Nuevos Patrones de Diseo 34 DAO con Abstract Factory RESUMEN: Utilizando DAO en combinacin con Abstract Factory se logra Independizar totalmente a las clases de la capa lgica del mecanismo concreto de persistencia teniendo adems bajo acoplamiento y alta cohesin (ventaja heredada del patrn DAO). Facilitar notoriamente la migracin entre diferentes mecanismos de persistencia mediante la incorporacin de nuevas clases DAO, sin tener que modificar el cdigo de las clases DAO ya existentes (ventaja heredada del patrn Abstract Factory). Cada mecanismo de persistencia es una familia de productos del Abstract Factory, mientras que las diferentes interfases DAO definidas representan los distintos tipos de producto. 18 Nuevos Patrones de Diseo 35 Conclusiones Finales Los patrones Value Object, Model View Controller, Abstract Factory y Data Access Object son la clave para lograr una separacin en tres capas (a nivel de diseo). La separacin entre la capa grfica y la capa lgica est dada por la combinacin de Value Object (datatypes) con Model View Controller. La separacin entre la capa lgica y la capa de persistencia est dada por la combinacin de Data Access Object con Abstract Factory. Realizar diseos en tres capas (grfica, lgica, persistencia) al construir sistemas orientados a objetos busca favorecer el cumplimiento de cualidades tales como: Mantenibilidad Reusabilidad Extensibilidad Nuevos Patrones de Diseo 36 Conclusiones Finales Capa Grfica Capa Lgica Capa Persistencia Vistas Controladores Datatypes Modelo / Facade Paciente Consulta Datatypes Factory DAOPacientes DAOConsultas 19 Nuevos Patrones de Diseo 37 Ejercicios Ejercicio 1 Ejercicio 1: : Dado el siguiente diagrama de clases de implementacin: Modifique el diagrama propuesto considerando que ahora los productos y las ventas se van a persistir en una base de datos MySql (aplicacin del patrn DAO en forma pura) Nuevos Patrones de Diseo 38 Ejercicios Ejercicio 2 Ejercicio 2: : Modifique el diagrama que hizo en el ejercicio 1 de modo tal que productos y ventas se sigan persistiendo en una base de datos MySql, pero ahora se haga combinando el patrn DAO con el patrn Abstract Factory. Cuntas familias y cuntos tipos de productos se tienen? Ejercicio 3 Ejercicio 3: : Ahora suponga que tambin se desea tener la posibilidad de persistir productos y ventas en una estructura de archivos XML. Agregue al diagrama que hizo en el ejercicio 2 las clases/interfases que sean necesarias para reflejarlo. Cuntas familias y cuntos tipos de productos se tienen ahora? 20 Nuevos Patrones de Diseo 39 Ejercicios Ejercicio 4 Ejercicio 4: : Dado el siguiente diagrama de clases de implementacin: ALUMNO - documento : integer - nombre : string - domicilio : string - telfono : integer - col A : Aprobaciones CURSO - cdigo : integer - nombre : string - duracin : real - rgimen : string * APRUEBA - fecha : date - calificacin : integer - cur : Curso << coleccin >> ALUMNOS - tabla : Hashtable << coleccin >> APROBACIONES - lista : List << coleccin >> CURSOS - tabla : Hashtable * * * 1 1 Nuevos Patrones de Diseo 40 Ejercicios Ejercicio 4 (continuaci Ejercicio 4 (continuaci n) n): : Haga lo solicitado en el ejercicio 1 pero aplicndolo al diagrama propuesto en la diapositiva anterior. Ejercicio 5 Ejercicio 5: : Haga lo solicitado en el ejercicio 2 pero aplicndolo al diagrama dibujado por usted en el ejercicio 4. Ejercicio 6 Ejercicio 6: : Haga lo solicitado en el ejercicio 3 pero aplicndolo al diagrama dibujado por usted en el ejercicio 5.