Mostrando entradas con la etiqueta Patrones de Diseño. Mostrar todas las entradas
Mostrando entradas con la etiqueta Patrones de Diseño. Mostrar todas las entradas

viernes, 18 de septiembre de 2015

Programación Reactiva: Reactive Extensions Library o RX (comenzado a aprender esta cosa de Reactive)



Encontré un buen video explicando la programación reactiva en el sabor de la librería Reactive Extensions hecho por Jafar Husain (Líder técnico en Netflix) . Aquí un pequeño resumen de los principales conceptos que extraje.

Event Reacting
El término reactivo se puede asociar por analogía con alguien tirando un montón de bolas hacia uno al mismo tiempo. Tienes que reaccionar rápidamente para tratar de atraparlas todas. Uno no tiene control sobre cuándo quiere que las pelotas sean lanzadas, sólo hay que estar preparado para la captura de ellas.

Para empezar a entender Rx (Reactive Extensions), necesitamos tener en mente dos patrones fundamentales de diseño en consideración: Iterador y Observador.

En Java tenemos la interfaz Iterable, que es una interfaz que cualquier tipo de colección puede implementar para permitir a los consumidores de esa colección obtener objetos uno a la vez. Así es como en Java podemos utilizar el operador foreach para recorrer una colección. Debido a que la interfaz proporciona un específico Iterator para el tipo de datos que estamos consumiendo en la colección.

Tres cosas pueden pasar cuando usamos un iterador:
  1. Obtener el siguiente item
  2. No hay más items para consumir (fin del flujo  de datos)
  3. Un error sucedió (en Java mediante el uso de excepciones)
El patrón Obervador es otro famoso patrón de diseño donde subscriptores se subscriben a ciertos temas para poder ser notificados cuando un cambio ocurre. Si analizamos este patrón encontramos que es muy similar al patrón Iterador en el sentido que existe un productor y un consumidor; la principal diferencia es que en el Observador el productor está en control de cuando enviar los datos, mientras que en el Iterador el consumidor es el que está en control. El es el que decide cuando tomar los datos del productor.

Iteratot Observer Fusion
Pero hay dos aspectos principales "ausentes" del patrón Observador que si están presentes en el patrón Iterador:
  • Una manera de indicar que no hay más datos
  • Una manera de indicar que un error acaba de suceder
En el Observador uno solamente puede subscribir un callback para recibir datos, pero no puede registrar callbacks para el evento donde se indica que no hay más datos siendo encolados en el stream (evento de completado), o para indicar que un error sucedió. RX trata básicamente de unificar el "acueducto" del Observador con el "acueducto" del Iterador produciendo un nuevo tipo llamado el Observable, el cual proporciona la misma semántica de ambos patrones.

Hay un montón de operaciones interesantes que se pueden realizar sobre los iterables. En SQL existen un gran número de operaciones que se pueden hacer sobre conjunto de datos: filtrar, seleccionar, ordenar, etc. Que tal si todas esas mismas operaciones que podemos ejecutar sobre datos alojados en una tabla, pudiéramos también hacerlo sobre eventos (datos que llegan). Eso no es una fantasía con RX. Es posible escribir sentencias al estilo SQL sobre eventos. La diferencia es que la consulta es evaluada conforme llegan los datos. Uno puede evaluar datos en tiempo real.

Cada vez más el código se esta volviendo orientado a eventos. Tenemos un montón de llamadas asincrónicas en el lado del cliente (JS), y del lado del servidor (Node.JS). Esto agrega mucha complejidad en la aplicación que intenta manejar todos estos callbacks. RX provee el nuevo tipo de datos Observable para establecer una manera poderosa de modelar eventos. Al completar las semánticas faltantes (fin de flujo de datos y error), ahora uno puede aplicar operaciones familiares que hacemos sobre colecciones como map, filter, reduce, merge, zip, etc. Todas estas cosas podemos hacerlas sobre flujo de datos también.

Imaginemos la nueva Java 8 Stream API sobre datos que llegan dinámicamente por eventos lanzados hacia uno. Muchas cosas pueden mejorar en la aplicación como enviar datos al consumidor mucho más rápido, por citar solo un ejemplo.

Aún necesito aprender mucho más de este nuevo paradigma, pero al menos pienso que este video cubre los conceptos fundamentales que necesitamos tener de antemano antes de arrojarnos a jugar con RX en el código.

lunes, 27 de febrero de 2012

Libro de Patrones de Diseño en JavaScript

Quisiera recomendar este libro publicado de manera gratuita por Addy Osmani, reconocido JavaScript Ninja que aborda el tema de Patrones de Diseño en JavaScript. Apenas he comenzado a leerlo, pero hasta ahora da buenos signos de ser una buena lectura.

Existe una tendencia como desarrolladores en pensar que, como JavaScript no tiene un soporte robusto de POO, podemos programar sin mayor estructuración nuestra funcionalidad de Front-End. Lo básico es que las funciones de JavaScript se encuentren en archivos separados. No obstante es importante considerar que hay mecanismos para emular un desarrollo que haríamos con lenguajes más formales.



lunes, 30 de enero de 2012

Evaluación corta de Patrones de Diseño GoF


He estado dando un curso de patrones de diseño en la empresa en la que laboro como parte del programa de capacitación interna. Quiero dejar a disposición del público una prueba corta de 15 preguntas (en inglés) de selección única, para todo aquel que quiera evaluar que tanto conoce de los patrones clásicos de GoF.

lunes, 13 de junio de 2011

Patrón de diseño: Prototype


Estuve estudiando este patrón de diseño para enseñarlo en una clase. Este patrón en resumen permite crear nuevos objetos al hacer una copia exacta de un objeto prototípico.

El principal uso que le encuentro al patrón, al menos de primera impresión ya que los patrones se van digiriendo con el tiempo, es el ahorro que se logra en casos donde la instanciación de un objeto es costoso en términos de recursos del sistema (memoria, tiempo, procesamiento, etc).

En el ejemplo de abajo simulo esta aplicación. La clase Sheep (oveja) cuando se instancia require la construcción de su cadena de ADN; este es el supuesto proceso pesado. Java ya incluye una interface llamada "Cloneable" que cuando se implementa en una clase, las instancias de la clase se pueden clonar. Cuando hablamos de clonar nos referimos a crear una copia exacta de un objeto al dejar los mismos valores de sus atributos de clases en una instancia aparte.

La clase Sheep implementa por tanto esta interface con lo cual crear otra oveja similar es asunto simplemente de llamar el método "clone()".

public class Sheep implements Cloneable {

protected String dna;
protected final char [] DNA_ALPHABET = {'A','C','T','G'};

public String getDNA() {
return dna;
}

public Sheep() {
// Simulates extremely long process to create a sheep.
StringBuilder dnaBuilder = new StringBuilder();

for(int i=0; i < 30; i++) {
int alphabetIndex = (int) (Math.random() * 10 % 4);
dnaBuilder.append(DNA_ALPHABET[alphabetIndex]);
}
dna = dnaBuilder.toString();
}

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}

public static void main(String[] args) {
Sheep dolly = new Sheep();
System.out.println("Dolly was born: " + dolly.getDNA());

try {
Sheep dollyClon = (Sheep) dolly.clone();
System.out.println("Dolly Clon: " + dollyClon.getDNA());

} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

Resultado:

Dolly was born: CAAACGGGACACTTATACTTGTTCGCCCAT
Dolly Clon: CAAACGGGACACTTATACTTGTTCGCCCAT

lunes, 24 de enero de 2011

Patrón de Diseño: Decorator

Como leía en un libro, asimilar patrones de diseño es como tener varios expertos sentados a tu lado mientras programas. Quize en esta ocasión agregar un experto más a mi lado comprendiendo el patrón "Decorator" (o Decorador en español). En especial llamó mi atención este patrón, porque leyendo un libro mencionaron que las operaciones en Java de I/O streaming utilizan este patrón por detrás para asignar funcionalidad en tiempo de ejecución.


File file = new File("c:/temp");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);


En muy resumidad palabras, el uso del patrón Decorator es más flexible que el uso de herencia, en el sentido de que la herencia asigna responsabilidad a clases en tiempo de compilación, mientras que el patrón Decorator las asigna en tiempo de ejecución ("runtime"). Como en casi todo patrón de diseño lo más sencillo es explicarlo directamente con código.

En el siguiente código de ejemplo vamos utilizar una aplicación hipotéctica de una academia de ingeniería de software. En dicha aplicación existe la entidad estudiante de ingeniería de software ("SoftwareEngineerStudent"), a la cual se le puede aplicar N decoradores para simular el proceso de aprendizaje durante su carrera. He aquí el patrón aplicado en UML:




El SoftwareEngineerStudent es representado con una interface donde el método que vamos a decorar en otras clases es el showCurriculum():


package patterns.decorator;

public interface SoftwareEngineerStudent {

/**
* Shows students curriculum.
*/
public void showCurriculum();
}


Seguidamente tenemos la clase que implementa nuestra anterior interface, el "SimpleSoftwareEngineerStudent". La única funcionalidad agregada es que imprimimos en consola que "Soy un estudiante de Ingeniería de Software":


package patterns.decorator;

public class SimpleSoftwareEngineer implements SoftwareEngineerStudent{

public void showCurriculum() {
System.out.println("I'm a software engineer student");
}

}


Ahora definimos nuestra clase decoradora base con el objetivo de permitir el poliformismo a la hora de entrenar (decorar) a los futuros estudiantes:


package patterns.decorator;

/**
* The Base decorator class.
*
*/
public abstract class SoftwareEngineerDecorator implements SoftwareEngineerStudent{

protected SoftwareEngineerStudent softwareEngineerStudent;

/**
* Constructor must receive an instance of SoftwareEngineerStudent so it can decorates
* something.
*/
public SoftwareEngineerDecorator(SoftwareEngineerStudent softwareEngineerStudent) {
this.softwareEngineerStudent = softwareEngineerStudent;
}

/**
* The operation that will be decorated by subclasses.
*/
public void showCurriculum() {
this.softwareEngineerStudent.showCurriculum();
}
}


Seguidamente creamos nuestras clases decoradoras extendiendo de la clase base SoftwareEngineerDecorator. Nótese que lo único que se hace es sobre escribir el método "showCurriculum()" para imprimir el conocimiento anterior junto con el nuevo (o decorado):


package patterns.decorator;

public class JavaProgrammingDecorator extends SoftwareEngineerDecorator {

public JavaProgrammingDecorator(
SoftwareEngineerStudent softwareEngineerStudent) {
super(softwareEngineerStudent);
}

public void showCurriculum() {
super.showCurriculum();
System.out.println("I have some basic knowledge in Java language.");
}

}

public class DesignPatternsDecorator extends SoftwareEngineerDecorator{

public DesignPatternsDecorator(
SoftwareEngineerStudent softwareEngineerStudent) {
super(softwareEngineerStudent);
}

public void showCurriculum() {
super.showCurriculum();
System.out.println("I know how to apply the most common design patterns");
}
}


public class DatabasesDecorator extends SoftwareEngineerDecorator {

public DatabasesDecorator(SoftwareEngineerStudent softwareEngineerStudent) {
super(softwareEngineerStudent);
}

public void showCurriculum() {
super.showCurriculum();
System.out.println("I can design ER databases.");
System.out.println("I know SQL.");
}
}



Por último tenemos la clase "SoftwareEngineerAcademy" para mostrar la aplicación de los decoradores. En el método principal se instancian dos estudiantes los cuales toman diferentes caminos en sus carreras. Los métodos trainIn...() toman la instancia del estudiante y le aplican un decorador que es retornado al final. Este mecanismo podría mejorarse aplicando un poco de reflection, pero vamos a dejarlo así para simplificar la explicación.



package patterns.decorator;

public class SoftwareEngineerAcademy {

public static SoftwareEngineerStudent trainInJava(
SoftwareEngineerStudent softwareEngineerStudent) {
SoftwareEngineerStudent studentTrainedInJava =
new JavaProgrammingDecorator(softwareEngineerStudent);
return studentTrainedInJava;
}

public static SoftwareEngineerStudent trainInDatabases(
SoftwareEngineerStudent softwareEngineerStudent) {
SoftwareEngineerStudent studentTrainedInDatabases =
new DatabasesDecorator(softwareEngineerStudent);
return studentTrainedInDatabases;
}

public static SoftwareEngineerStudent trainInDesignPatterns(
SoftwareEngineerStudent softwareEngineerStudent) {
SoftwareEngineerStudent studentTrainedInDesignPatterns =
new DesignPatternsDecorator(softwareEngineerStudent);
return studentTrainedInDesignPatterns;
}

public static void main(String[] args) {
SoftwareEngineerStudent studentA = new SimpleSoftwareEngineer();
studentA = SoftwareEngineerAcademy.trainInJava(studentA);
studentA = SoftwareEngineerAcademy.trainInDatabases(studentA);
System.out.println("**********************");
System.out.println("Student A Curriculum: ");
System.out.println("**********************");
studentA.showCurriculum();

SoftwareEngineerStudent studentB = new SimpleSoftwareEngineer();
studentB = SoftwareEngineerAcademy.trainInJava(studentB);
studentB = SoftwareEngineerAcademy.trainInDesignPatterns(studentB);
System.out.println("**********************");
System.out.println("Student B Curriculum: ");
System.out.println("**********************");
studentB.showCurriculum();
}
}


Cuando corremos el programa vemos que efectivamente cuando se aplicaron los decoradores, al final el estudiante muestra el curriculum correctamente:


**********************
Student A Curriculum:
**********************
I'm a software engineer student
I have some basic knowledge in Java language.
I can design ER databases.
I know SQL.
**********************
Student B Curriculum:
**********************
I'm a software engineer student
I have some basic knowledge in Java language.
I know how to apply the most common design patterns

miércoles, 19 de enero de 2011

Cascarones UML para patrones de diseño



Hasta hace poco descubrí que la herramienta de código abierto StarUML para edición de diagramas UML, tenía una opción tipo wizard para generar cascarones de los patrones de diseño más conocidos.

1. Clic derecho en el diagrama - > Apply Pattern...


2. En la siguiente ventana aparecen dos grupos de patrones: EJB y los clásicos GoF ("Gang of Four"). Elegimos el que deseamos:



3. En la ventana de parámetros podemos cambiar el nombre de las clases que forman el patrón o incluso escoger clases que ya existen en nuestro proyecto.


En este ejemplo agregue 4 patrones de diseño de esta forma al diagrama.

lunes, 11 de octubre de 2010

Ejemplo Sencillo del patrón Singleton

El patrón Singleton es quizás uno de los patrones más básicos que deberíamos entender por su extendida aplicabilidad en el diseño de clases, y también porque es uno de los patrones más usados en las entrevistas para probar conocimientos en patrones de diseño.

No pretendo extenderme mucho en la descripción del patrón. La definición más simple es que provee una sola instancia de un objeto para una determinada clase. A continuación dejo un ejemplo de cómo implementar este patrón:


/**
* Simple example of the Singleton pattern.
* @author gsolano
*
*/
public class MySingleton {

   /**
   * The only instance that is going to be used.
   */
   protected static MySingleton myInstanceOfSingleton;
   protected String fooValue;

   /**
   * Gets the instance of this class.
   * @return
   */
   public static MySingleton getInstance() {
      if(myInstanceOfSingleton == null) {
         synchronized (MySingleton.class) {
            myInstanceOfSingleton = new MySingleton();
         }
      }
      return myInstanceOfSingleton;
   }

   /**
   * Private constructor to prohibit instantiation of this class.
   */
   private MySingleton() {
      fooValue ="Hello World!!";
   }

   public void setFooVaule(String value) {
      this.fooValue = value;
   }

   public void printFooValue() {
      System.out.println(fooValue);
   }

   public static void main(String[] args) {
      MySingleton mySingleton = MySingleton.getInstance();
      mySingleton.printFooValue();
      mySingleton.setFooVaule("This is Singleton!!");

      MySingleton mySecondSingleton = MySingleton.getInstance();
      // Prints the  value set in mySingleton variable.
      mySecondSingleton.printFooValue();
   }
}
Salida:



Cabe resaltar dos aspectos importantes de este ejemplo de Singleton.
  1. El constructor es declarado como privado para prohibir crear muútiples instancias de esta clase.
  2. La única puerta para acceder la clase es a través del método “getInstance” que internamente instancia una sola vez el objeto que se utilizará en todo el contexto de la aplicación, y el resto de las veces retorna una referencia a la única instancia de la clase.

viernes, 20 de agosto de 2010

Uso del patrón Observador para llevar el progreso durante la carga de la página

¿Alguna te ha tocado entrar a una página que corre por detrás un proceso pesado y que por tanto dura bastante en terminar? Si la página no está bien diseñada en términos de experiencia de usuario, terminamos con una sensación de que va tomar toda la eternidad en cargar.

Si queremos evitar este efecto tortuga en nuestros sitios, debemos considerar siempre si podemos agregar un indicador en la página que muestre el progreso avanzado de la tarea que está corriendo el servidor. Para lograr esto podemos tomar ventaja del patrón Observador.

Lo que necesitamos primariamente es correr el proceso de manera asincrónica, en otras palabras, en un hilo diferente. El siguiente diagrama muestra como el proceso pesados en contenido en una clase que extiende de "Thread".




La clase de abajo va a simular un proceso que toma mucho tiempo al tomar varias siestas (Thread.sleep()).

package com.gsolano.longprocess

import java.util.Observable;

/**
* Class with an observable mock long progress.
* @author gsolano
*
*/
public class LongProcess extends Observable {

/**
* Keeps the progress of the process.
*/
protected Float progress;

/**
* Simulates a long process.
*/
public void start() {
int n =10;
for (int i=0;i <= n; i++) {
progress = (float)i/(float)n * 100; // Calculates progress.
try {
Thread.currentThread();
Thread.sleep(2000);
this.setChanged();
this.notifyObservers(progress);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


El progreso de esta clase es calculado en cada iteración notificando al mismo tiempo del cambio que se ha efectuado en el progreso. La siguiente clase es la que observa a "LongProcess".

package com.gsolano.longprocess;

import java.util.Observable;
import java.util.Observer;

/**
* Observer class
*
* @author gsolano
*/
public class LongProcessObserver implements Observer{

protected Float progress;
/**
* Tracks the progress of the long process.
* @return
*/
public Float getProgress() {
return progress;
}

public void update(Observable o, Object arg) {
progress = (Float) arg;
}
}


Para completar el diagrama anterior, necesitamos crear una clase que extienda de Thread para envolver el proceso largo y lanzarlo en un hilo diferente.

/**
*
* Class to run a LongProcess in a separate thread.
*
* @author gsolano
*
*/
public class LongProcessThread extends Thread {

private LongProcess longProcess;

public LongProcess getLongProcess() {
return longProcess;
}

public void setLongProcess(LongProcess longProcess) {
this.longProcess = longProcess;
}

@Override
public void run() {
if(longProcess != null) {
longProcess.start();
}
}
}


Ahora brinquemos a al lado de la parte web. En el siguiente action de struts se maneja dos tipos de eventos:

1. Arranque el proceso pesado:

  • El proceso pesado es creado.
  • Un observador es agregado al proceso.
  • Lance el proceso en un hilo distinto.
  • Se guarda el observador en sesión.

2. Enviar una actualización del progreso del proceso

  • Se saca el observador de la sesión.
  • El valor de progreso se toma del observador y se escribe en la respuesta.

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class FooProgressAction extends Action{

@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {

String action = request.getParameter("action");

if(action != null) {
if(action.equalsIgnoreCase("progress")) { // If action is ajax request to get progress.
// Get the observer.
LongProcessObserver longProcessObserver = (LongProcessObserver)
request.getSession().getAttribute("observer");
if(longProcessObserver != null) {
// Get the progress from the observer.
Float progress = longProcessObserver.getProgress();
if(progress != null) {
// Send the progress to the page.
response.getWriter().write(progress.toString());
}
return null;
}
} else if(action.equalsIgnoreCase("start")) { // Did someone click the start button?
launchLongProcess(request);
}
}
return mapping.findForward("success");
}

private void launchLongProcess(HttpServletRequest request) {
LongProcess longProcess = new LongProcess();
LongProcessObserver observer = new LongProcessObserver();
// Add the observer to the long process.
longProcess.addObserver(observer);
// Launch long process in a thread.
LongProcessThread longProcessThread = new LongProcessThread();
longProcessThread.setLongProcess(longProcess);
longProcessThread.start();
// Keep the observer in session.
request.getSession().setAttribute("observer", observer);
// Send a flag indicating that party just started!
request.setAttribute("processStarted", true);
}
}
Del lado del cleinte se necesita solamente lógica para comenzar un ciclo de invocaciones Ajax para solicitar el progreso hasta que se alcance el 100%.


<%@ taglib uri="/WEB-INF/tld/c.tld" prefix="c" %>

<html>
<head>
<script language="Javascript">
var seconds = 1;
var run = false;
var ajaxURL;

function checkProgress(url) {
if(typeof url != 'undefined') {
ajaxURL = url;
}

var xmlHttp;
try {
xmlHttp = new XMLHttpRequest(); // Firefox, Opera 8.0+, Safari
} catch (e) {
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); // Internet Explorer
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
alert("Ajax not supported");
return false;
}
}
}
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 ) {
var progress = xmlHttp.responseText;
if(progress == 100.0) {
document.getElementById('progress').innerHTML = "Finished!!";
return;
}else {
if(progress) {
document.getElementById('progress').innerHTML = progress + "%";
}
setTimeout('checkProgress()', seconds * 1000);
}
}
};
xmlHttp.open("GET", ajaxURL, true);
xmlHttp.send(null);
}
</script>
</head>
<body>
<div style="position: absolute; left:40%; text-align:center; border: 1px solid; margin: 20px; padding:20px; width: 150px;">
<form action="${pageContext.request.contextPath}/longProcess.do">
<input type="hidden" name="action" value="start" />
<input type="submit" value="Start!" />
</form>

<div id="progress"></div>

<c:if test="${not empty processStarted}">
<script language="Javascript">
setTimeout('checkProgress(\'${pageContext.request.contextPath}/longProcess.do?action=progress\')', 1000);
</script>
</c:if>
</div>
</body>
</html


Resultado:




martes, 2 de marzo de 2010

Tres Amigos de Persistencia (DAO - DAOFactory - BO)

Hoy en día, aún con todo el buen conocimiento acumulado que hay en patrones de diseño, ingenieros de software todavía tienden (tendía hasta hace un tiempo :)) a programar clases donde la lógica de negocio esta mezclada con la lógica de persistencia. Inclusive los más experimentados desarrolladores omiten este aspecto importante de la arquitectura de software, quizás por falta de conocimiento, o talvez solo por la prisa de sacar una funcionalidad rápidamente. Me parece que la última razón es la más común.

¿Por qué deberíamos molestarnos tanto con esta separación? Bien, la idea no es ahondar mucho en lo que es básico en arquitectura de software: programación en multicapas, sino más bien ayudar con algunos patrones útiles para cumplir con este aspecto de una aplicación bien diseñada. Creo que la mayoría de los desarrolladores están concientes de este principio de separación entre capa de lógica y capa de persistencia, pero lo que tal vez no es tan obvio es cómo se logra codificar las clases para lograr esta independencia.


Primero vamos a comenzar con el patrón más conocido: DAO por sus siglas en inglés de "Data Access Object" u Objeto de Acceso de Datos. Citando de un libro de patrones de diseño [1]:

Problema:

Usted quiere encapsular el acceso y manipulación de datos en una capa separada.

Fuerzas:

  1. Usted desea implementar los mecanismos de acceso a datos para accesar y manipular los datos en el almacenamiento de persistencia.
  2. Usted quiere desacoplar la implementación del almacenamiento de persistencia del resto de su aplicación.
  3. Usted quiere proveer una API uniforme de acceso a datos para un mecanismo de persistencia con varios tipos de fuentes de datos (data sources),tales como: RDBMS, LDAP, OODB, repositorios XML, archivos planos, etc.
  4. Usted quiere organizar la lógica de acceso de datos y encapsular las características propietarias para facilitar el mantenimiento y la portabilidad.

Las clases DAO contiene toda la lógica para conectar por ejemplo una base de datos y obtener todos los datos necesarios correspondientes a las tablas. Un aspecto importante de cualquier clase DAO es que la implementación de cualquier método que retorne datos, nunca debe retornar tipos relacionados con el propietario de almacenamiento. Por ejemplo una implementación DAO de JDBC nunca debe retornar un ResultSet, de lo contrario no se estaría cumpliendo con el punto #4. La aplicación se acoplaría con la implementación de JDBC. Por eso es que en el siguiente diagrama UML el objeto retornado de la clase DAO es un "TransferObject" (Objeto de transferencia). No hay que detallar mucho en este último patrón sino solamente mencionar que tan solo retornando objetos de dominio es suficiente para asegurarse el desacoplo de la capa de negocio con la de persistencia.





El implementar el patrón DAO en nuestra aplicación no es suficente para desacoplar 100% nuestra capa de negocio con la de persistencia. Imaginemos que tenemos la siguiente clase DAO y su cliente que la consume:


package com.foo.dao.jdbc;

class FooDAOJDBCImpl {
public void updateFoo(Foo foo) {
...
}
}

public class FooClient {
void updateChangesInFoo(Foo foo) {
com.foo.dao.jdbc.FooDAOJDBCImpl fooDAOJDBCImpl = new com.foo.dao.jdbc.FooDAOJDBCImpl();
fooDAOJDBCImpl.update(foo);
}
}

Nótese que el cliente necesita instanciar directamente la implementación JDBC. Aún cuando es oculto para el cliente cómo la clase DAO internamente actualiza los datos en el almacenamiento de persistencia, el cliente aún puede conocer que la implementación utiliza JDBC para persistir los datos. Si la implementación de JDBC es reemplazada por otra, el código del cliente tendrá que ser actualizado para usar las nuevas clases DAO.

Para hacer nuestro diseño más flexible a este tipo de cambios, y también para preparar nuestro código para unidades de prueba (uso de clases Mock), podemos unir en matrimonio nuestro patrón DAO con el conocidísimo AbstractFactory para tener un hijo llamado DAOFactory. La clase DAOFactory usa reflexión (en esta versión del patrón) para instanciar las clases DAO.





public interface DAO {

}

public interface FooDAO extends DAO {
public void update(Foo foo);
}

package com.foo.dao.jdbc;
public class FooJDBCImpl implements FooDAO {
public void update(Foo foo) {
....
}
}

public class FooClient {
void updateChangesInFoo(Foo foo) {
FooDAO dao = (FooDAO) DAOFactory.getDAO("foo") ;
dao.update(foo);
}
}



No es hasta el tiempo de ejecución que se conoce cuál clase DAO será usada para ejecutar la función de persistencia. El nombre de las clases se pueden guardar en un archivo de propiedades. Si la implementación de la clase DAO cambia, será totalmente trasparente para la clase cliente.

/**
Properties in some file:
foo=com.foo.dao.jdbc.FooDAOJDBCImpl
foo2=com.foo.dao.jdbc.Foo2DAOJDBCImpl

**/

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class DAOFactory {

private static Properties props = new Properties();
private static boolean loadedProperties = false;
private static String propertiesPath;

public static void init(String propertiesPath) {
propertiesPath = path;
}

public static DAOFactory getDAO(String name) {
try {

Class daoClass = Class.forName( getClass( name ) );
return (DAO) daoClass.newInstance();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
catch (Exception e) {
e.printStackTrace();
return null;
}

}

private static String getClass( String propertyName ) {
String className = null;
try {

if ( !loadedProperties ) {

FileInputStream file = new FileInputStream( propertiesPath );
props.load( file );
loadedProperties = true;
}

className = props.getProperty( propertyName, "");
if ( className.length() == 0)
return null;
}
catch ( FileNotFoundException e) {
e.printStackTrace();
}
catch ( IOException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
return className;
}

}
El último patrón de diseño para completar nuestra banda es el "Business Object" (BO) u Objeto de Negocio. Aún estoy aprendiendo a usarlo correctamente; mientras escribía este artículo me di cuenta que necesito hacer algunos arreglos a una implementación que tengo de este patrón. De todas maneras, uno de los principales propositos de este patrón es separar la lógica de persistencia con la lógica de negocio. Normalmente en nuestas aplicaciones tenemos modelos conceptuales algo complejos conteniendo objetos interrelacionados, estructurados y compuestos. Estas relaciones de objetos compuestos requieren bastante lógica solo para los mecanismos de persistencia. Así que para evitar mezclar estos dos tipo de lógicas, se agrega una capa intermedia
entre la lógica de negocios y la capa de acceso de datos; la capa de objetos de negocio.

Supongamos que tenemos una clase Foo conteniendo una lista de objetos tipo Foo2:

public class Foo {
private List<Foo2> foo2s;
private String someAttribute;

}
public class Foo2 {
private String someAttribute;
}


Si queremos persistir nuestra clase Foo creamos dos clases BO. La clase FooBO in el principal punto de entrada para guardar todos los objetos compuestos:


public class FooBO {

public void saveFoo(Foo foo) {
FooDAO fooDAO = (FooDAO) DAOFactory.getDAO("foo") ;
fooDAO.saveBasicFooInfo(foo);
Foo2BO foo2Bo = new Foo2BO();

for (Foo2 foo2 : foo.getFoo2s()) {
foo2Bo.saveFoo2(foo2);
}
}
}

public class Foo2BO {
public void saveFoo2(Foo foo) {
FooDAO2 fooDAO2 = (FooDAO2) DAOFactory.getDAO("foo2") ;
fooDAO2.saveFoo2(foo2);
}
}

Pueden haber distintas maneras de implementar cualquiera de los tres patrones descritos en este artículo; nada está escrito en piedra. La idea era dar un vistazo rápido en estos tres patrones. Comentarios son bienvenidos.

[1] Deepak Alur, John Crupi, Dan Malks. "Core J2EE Patterns, Best Practices and Design Strategies", 2003. Pags: 462,463.