Catálogo de Patrones de Diseño J2EE. Y II: Capas de Negocio y de Integración

El cliente le pide al servicio una lista de �tems para su presentaci�n. El n�mero de �tems de la lista no se conoce y puede ser muy grande en muchas circunstancias.

.�Problema

La mayor�a de las aplicaciones de la plataforma J2EE tienen un requerimiento de b�squeda y consulta para buscar y listar ciertos datos. En algunos casos, dichas operaciones de b�squeda y consulta podr�an traer resultados que pueden ser bastante grandes. No es pr�ctico devolver toda la hoja de resultados cuando los requerimientos del cliente son moverese por los resultados, en vez de procesar el conjunto completo. Normalmente, un cliente usa los resultados de una consulta para prop�sitos de s�lo-lectura, como mostrar una lista de resultados. Muchas veces el cliente s�lo ve los primeros registros que devuelve la b�squeda, y podr�a descargar los registros restantes e intentar otra nueva consulta. La pr�ctica de obtener uns lista de valores representados en un bean de entidad llamando al m�todo ejbFind(), que devuelve una collection de objetos remotos, y luego llamar a cada bean de entidad para obtener el valor, consume muchos recursos de red y se considera una mala pr�ctica.

Hay algunas consecuencias asociadas con el uso de los m�todos b�scadores de EJB que resultan en grandes conjuntos de datos. Cada implementaci�n de contenedor tiene una cierta cantidad de m�todos b�scadores para crear una colecci�n de referencias EJBObject. El rendimiento del m�todo de b�squeda var�a, dependiendo de la implementaci�n del contenedor. De acuerdo a la especificaci�n EJB, un contenedor podr�a invocar a m�todos ejbActivate() sobre entidades encontradas con los m�todos de b�squeda. Como m�nimo, un m�todo de b�squeda devuelve las claves primarias de las entidades encontradas, que el contenedor devuelve al cliente como una colecci�n de referencias EJBObject. Este comportamiento se aplica para todas las implementaciones de contenedores. Algunas implementaciones podr�an introducir una sobrecarga de m�todos de b�squeda asociando los ejemplares de beans de entidad a esos ejemplares EJBObject para darle acceso al cliente a esos beans de entidad. Sin embargo, este es un uso pobre de los recursos si el cliente no est� interesado en acceder al bean o en invocar sus m�todos. Esta sobrecarga puede impedir el rendimiento de la aplicaci�n si �sta incluye consultas que producen muchos resultados.

.�Causas

  • La aplicaci�n cliente necesita una facilidad de consulta eficiente para evitar tener que llamar al m�todo ejbFind() de cada bean e invocar a cada objeto remoto devuelto.
  • Se necesita una mecanismo de cach� en la capa-servidor para servir a los clientes que no pueden recibir y procesar el cojunto de resultados completo.
  • Se puede optimizar una consulta que se ejecuta repetidamente sobre unos datos razonablemente est�ticos para proporcionar resultados m�s r�pidos. Esto depende de la aplicaci�n y de la implementaci�n de este patr�n.
  • Los m�todos de b�squeda EJB no son apropiados para navegar por tablas enteras en la bases de datos o para buscar grandes conjuntos de resultados en una tabla.
  • Los m�todos de b�squeda se podr�an considerar sobrecargados cuando se utilizan para encontrar grandes cantidades de resultados. El contenedor podr�a crear un gran n�mero de objetos de infraestructura para facilitar las b�squedas.
  • Los m�todos de b�squeda EJB no sin apropiados para realizar cach� de resultados. El cliente podr�a no ser capaz de manejar el conjunto completo de resultados en una s�la llamada. Si es as�, el cliente podr�a necesitar un cach� en el lado del cliente y funciones de navegaci�n para moverese por el conjunto de resultados.
  • Los m�todos de b�squeda EJB tiene consultas predeterminadas y ofrecen una flexibilidad m�nima. La especificaci�n EJB 2.0 permite un lenguaje de consulta, EJB QL, para beans de entidad manejados por el contenedor. EJB QL hace m�s f�cil escribir m�todos de b�squeda portables y ofrece una mayor flexibilidad.
  • El cliente quiere moverse hacia adelante o hacia atr�s dentro de la hoja de resultados.

.�Soluci�n

Utilizar un Value List Handler para controlar la b�squeda, hacer un cach� con los resultados, y proporcionar los resultados al cliente en una hoja de resultados cuyo tama�o y desplazamiento cumpla los requerimientos del cliente.

Este patr�n crea un ValueListHandler para controlar la funcionalidad de la ejecuci�n de la consulta y el cach� de resultados. El ValueListHandler accede directamente a un DAO que puede ejecutar la consulta requerida. El ValueListHandler almacena los resultados obtenidos del DAO como una colecci�n de Transfer Objects. El cliente le pide al ValueListHandler que proporcione resultados de la consulta seg�n los necesite. El ValueListHandler implementa un patr�n Iterator [GoF] para proporcionar la soluci�n.

.�Estructura

La siguiente figura muestra el diagrama de clases para el patr�n Value List Handler:

.�Participantes y Colaboraciones

La siguiente figura muestra el diagrama de secuencia del patr�n Value List Handler:

.�ValueListIterator

Este interface podr�a proporcionar la facilidad de iteraci�n con los siguientes m�todos de ejemplo:

  • getSize() obtiene el tama�o de la hoja de resultados.
  • getCurrentElement() obtiene el Transfer Object actual de la lista.
  • getPreviousElements(int howMany) obtiene una colecci�n de Transfer Objects que son anteriores en la lista al elemento actual.
  • getNextElements(int howMany) obtiene una colecci�n de Transfer Objects que son posteriores en la lista al elemento actual.
  • resetIndex() reinicia el �ndice para ir al inicio de la lista.

Dependiendo de las necesidades, se pueden incluir otros m�todos de conveniencia en el interface ValueListIterator.

.�ValueListHandler

Este es el objeto que implementa el interface ValueListIterator. ValueListHandler ejecuta la consulta requerida cuando se lo solicita el cliente. Obtiene los resultados de la consulta, que maneja en una colecci�n privada representada por el objeto ValueList. ValueListHandler crea y manipula la colecci�n ValueList. Cuando el cliente solicita los resultados, ValueListHandler obtiene los Transfer Objects desde el ValueList cacheado, crea una nueva colecci�n de Transfer Objects, serializa la colecci�n y la env�a de vuelta alcliente. ValueListHandler tambi�n sigue la pista del �ndice actual y del tama�o de la lista.

.�DataAccessObject

ValueListHandler puede hacer uso de un DataAccessObject para mantener separada la implementaci�n del acceso a la base de datos. DataAccessObject proporciona un API sencillo para acceder a la base de datos (o cualquier otro almacenamiento persistente), ejecuta consultas y recupera resultados.

.�ValueList

ValueList es una colecci�n (una lista) que contiene los resultados de la consulta. Los resultados se almacenan como objetos Transfer Objects. Si falla la consulta y no se devuelven resultados, entonces esta lista est� vac�a. El bean de sesi�n ValueListHandler puede almacenar ValueList para evitar repeticiones innecesarias de la consulta.

.�TransferObject

TransferObject representa una vista de un registro individual de los resultados de la consulta. Es un objeto serializable no modificable que proporciona un espacio para los datos de los atributos de cada registro.

.�Estrategias

.�Java Object

Se puede implementar el ValueListHandler como un objeto Java arbitario. En este caso, el ValueListHandler puede ser utilizado por cualquier cliente que necesite esa funcionalidad. Para aplicaciones que no usan beans enterprise, esta estrategia es muy �til. Por ejemplo se podr�a construir aplicaciones sencillas utilizando servlets, JSPs, Business Delegates, y DAOs. En este escenario, el Business Delegates puede utilizar un ValueListHandler implementado como un objeto Java para obtener una lista de valores.

.�Stateful Session Bean

Cuando una aplicaci�n utiliza beans enterprise en la capa de negocio, se podr�a preferir implementar un bean de sesi�n que utilice el ValueListHandler. En este caso, el bean de sesi�n simplemente se enfrenta a un ejemplar de ValueListHandler. As�, el bean de sesi�n se podr�a implementar como un bean de sesi�n con estado para contener la lista y su estado, y esto simplemente podr�a act�ar como una fachada o como un proxy.

.�Consecuencias

  • Proporciona Alternativas a los m�todos find() de EJB para Grandes Consultas
    Normalmente, un m�todo de b�squeda EJB es una forma cara de obtener una lista de �tems, ya que implica un n�mero de referencias EJBObject. Value List Handler implementa un bean de sesi�n que utiliza un DAO para realizar la consulta y para proporcionar una colecci�n de Transfer Objects que cumplen el criterio de b�squeda. Como los Transfer Objects tienen poca sobrecarga en comparaci�n con las referencias EJBObject y su infraestructura asociada, este patr�n proporciona beneficios cuando las aplicaciones clientes requieren grandes cantidades de resultados.
  • Crea un Cach� de Resultados de la Consulta en el Lado del Servidor
    Se necesita cachear los resultados obtenidos de la ejecuci�n de la consulta cuando un cliente debe mostrarlos en peque�as partes en vez de una gran lista. Sin embargo, no todos los clientes basados en navegador puede realizar dicho cach�. Cuando no pueden, el servidor debe proporcionar esta funcioalidad. El patr�n Value List Handler proporciona una facilidad de cach� en el bean de sesi�n para contener los resultados de la consulta. El conjunto de resultados es una colecci�n de Transfer Objects que se puede serializar si es necesario.
    Cuando el cliente solicita una colecci�n, o un subconjunto de una colecci�n, el baen manejador devuelve los resultados solicitados como una colecci�n serializada de Transfer Objects. El cliente recibe la colecci�n y ahora tiene una copia local de la informaci�n, que el cliente puede mostrar o procesar. Cuando el cliente necesita un subconjunto adicional de resultados, le solicita al manejador que devuelva otra colecci�n serializada que contenga los resultados necesarios. El cliente puede procesar la consulta en trozos m�s peque�os y manejables. El bean manejador tambi�n le proporciona al cliente facilidades de navegaci�n (anterior y siguiente) para que pueda moverse por la lista de la forma necesaria.
  • Proporciona una Mayor Flexibilidad de Consulta
    A�adir una nueva consulta podr�a requerir la creaci�n de un nuevo m�todo finder o modificar uno existente, especialmente cuando se utilizan beans de entidad manejados por el bean (con beans de entidad manejados por el bean, el desarrollador implementa los m�todos de b�squeda en la implementaci�n del bean). Con un bean de entidad manejado por el contenedor, el desarrollador especifica los m�todos finder del bean de entidad en el descriptor de despliegue del bean. Los cambios en una consulta para bean manejado por el contenedor requieren cambios en el descriptor de despliegue. Por lo tanto, estos m�todos no son muy adecuados cuando los requerimientos de la consulta cambian din�micamente. Podemos implementar un Value List Handler para que sea m�s flexible que los m�todos del EJB proporcionado facilidades de consultas din�micas, construyendo los argumentos de la consulta en el momento de la ejecuci�n utilizando plantillas de m�todos, etc. En otras palabras, un desarrollador de un Value List Handler puede implementar b�squeda inteligente y algoritmos de cach� sin verse limitado por los m�todos finder.
  • Mejora el Rendimiento de Red
    El rendimiento de la red se ve mejorado porque s�lo los datos solicitados, en vez de todos los datos, se env�an (serializados) al cliente sobre unas necesidades b�sicas. Si el cliente muestra los primeros resultados y luego abandona la consulta, no se ha ocupado el ancho de banda de la red, ya que los datos se almacenan en el servidor y nunca se han env�ado al cliente. Sin embargo, si el cliente procesa todos los resultados, hace varias llamadas remotas al servidor. Cuando el cliente conoce por adelantado que necesita todos los resultados, el bean manejador puede proporcionar un m�todo que los env�e al cliente en una sola llamada a m�todo, y no se utiliza el cach�.
  • Permite Atrasar las Transaciones del Bean de Entidad
    El cach� de los resultados en el lado del servidor permite minimizar la sobrecarga de la b�squeda y podr�a mejorar el control de transaciones. Cuando el cliente est� listo para procesar un bean de entidad, accede al bean dentro de un contexto de transaci�n definido por el caso de utilizaci�n. Por ejemplo, una consulta para mostrar una lista de libros utiliza un Value List Handler para obtener la lista. Cuando el usuario quiere ver los detalles de un libro, implica al bean de entidad del libro en la transaci�n.

.�C�digo de Ejemplo

.�Implementar el Value List Handler como un objeto Java

Consideremos un ejemplo donde se va a recuperar y mostrar una lista de objetos de negocio de un Proyecto. En este caso se puede aplicar el patr�n Value List Handler. En el siguiente ejemplo tenemos la clase ProjectListHandler, que es responsable de proporcionar la lista de proyectos. Esta clase extiende la clase base ValueListHandler, que proporciona la funcionalidad de iteraci�n gen�ricoa para todas las implementaciones de Value List Handler de esta aplicaci�n.


package corepatterns.apps.psa.handlers;

import java.util.*;
import corepatterns.apps.psa.dao.*;
import corepatterns.apps.psa.util.*;
import corepatterns.apps.psa.core.*;

public class ProjectListHandler
extends ValueListHandler {

  private ProjectDAO dao = null;
  // use ProjectTO as a template to determine
  // search criteria
  private ProjectTO projectCriteria = null;

  // Client creates a ProjectTO instance, sets the
  // values to use for search criteria and passes
  // the ProjectTO instance as projectCriteria
  // to the constructor and to setCriteria() method
  public ProjectListHandler(ProjectTO projectCriteria)
  throws ProjectException, ListHandlerException {
    try {
      this.projectCriteria = projectCriteria;
      this.dao = PSADAOFactory.getProjectDAO();
      executeSearch();
    } catch (Exception e) {
      // Handle exception, throw ListHandlerException
    }
  }

  public void setCriteria(ProjectTO projectCriteria) {
    this.projectCriteria = projectCriteria;
  }

  // executes search. Client can invoke this
  // provided that the search criteria has been
  // properly set. Used to perform search to refresh
  // the list with the latest data.
  public void executeSearch()
  throws ListHandlerException {
    try {
      if (projectCriteria == null) {
        throw new ListHandlerException(
          "Project Criteria required...");
      }
      List resultsList =
        dao.executeSelect(projectCriteria);
      setList(resultsList);
    } catch (Exception e) {
      // Handle exception, throw ListHandlerException
    }
  }
}

En el siguiente c�digo tenemos la clase ValueListHandler que implementa el interface del iterador gen�rico ValueListIterator, que veremos en siguientes listados.


package corepatterns.apps.psa.util;

import java.util.*;

public class ValueListHandler
implements ValueListIterator {

  protected List list;
  protected ListIterator listIterator;

  public ValueListHandler() {
  }

  protected void setList(List list)
  throws IteratorException {
    this.list = list;
    if(list != null)
      listIterator =  list.listIterator();
    else
      throw new IteratorException("List empty");
  }

  public Collection getList(){
    return list;
  }

  public int getSize() throws IteratorException{
    int size = 0;

    if (list != null)
      size = list.size();
    else
      throw new IteratorException(...); //No Data

    return size;
  }

  public Object getCurrentElement()
  throws IteratorException {

    Object obj = null;
    // Will not advance iterator
    if (list != null)
    {
      int currIndex = listIterator.nextIndex();
      obj = list.get(currIndex);
    }
    else
      throw new IteratorException(...);
    return obj;

  }

  public List getPreviousElements(int count)
  throws IteratorException {
    int i = 0;
    Object object = null;
    LinkedList list = new LinkedList();
    if (listIterator != null) {
      while (listIterator.hasPrevious() && (i < count)){
        object = listIterator.previous();
        list.add(object);
        i++;
      }
    }// end if
    else
      throw new IteratorException(...); // No data

    return list;
  }

  public List getNextElements(int count)
  throws IteratorException {
    int i = 0;
    Object object = null;
    LinkedList list = new LinkedList();
    if(listIterator != null){
      while(  listIterator.hasNext() && (i < count) ){
        object = listIterator.next();
        list.add(object);
        i++;
      }
    } /  / end if
    else
      throw new IteratorException(...); // No data

    return list;
  }

  public void resetIndex() throws IteratorException{
    if(listIterator != null){
      listIterator = list.ListIterator();
    }
    else
      throw new IteratorException(...); // No data
  }
  ...
}

Abajo tenemos el c�digo importante para acceder al objeto ProjectDAO, utilizado por ValueListHandler para ejecutar la consulta y obtener los resultados.


package corepatterns.apps.psa.dao;

public class ProjectDAO {
  final private String tableName = "PROJECT";

  // select statement uses fields
  final private String fields = "project_id, name," +
      "project_manager_id, start_date, end_date, " +
      " started, completed, accepted, acceptedDate," +
      " customer_id, description, status";

  // the methods relevant to the ValueListHandler
  // are shown here.
  // See Data Access Object pattern for other details.
  ...
  private List executeSelect(ProjectTO projCriteria)
  throws SQLException {

    Statement stmt= null;
    List list = null;
    Connection con = getConnection();
    StringBuffer selectStatement =   new StringBuffer();
    selectStatement.append("SELECT "+ fields +
          " FROM " + tableName + "where 1=1");

    // append additional conditions to where clause
    // depending on the values specified in
    // projCriteria
    if (projCriteria.projectId != null) {
      selectStatement.append (" AND PROJECT_ID = '" +
        projCriteria.projectId + "'");
    }
    // check and add other fields to where clause
    ...

    try {
      stmt = con.prepareStatement(selectStatement);
      stmt.setString(1, resourceID);
      ResultSet rs = stmt.executeQuery();
      list = prepareResult(rs);
      stmt.close();
    }
    finally {
      con.close();
    }
    return list;
  }

  private List prepareResult(ResultSet rs)
  throws SQLException {
    ArrayList list = new ArrayList();
    while(rs.next()) {
      int i = 1;
      ProjectTO proj = new
        ProjectTO(rs.getString(i++));
      proj.projectName = rs.getString(i++);
      proj.managerId = rs.getString(i++);
      proj.startDate = rs.getDate(i++);
      proj.endDate = rs.getDate(i++);
      proj.started = rs.getBoolean(i++);
      proj.completed = rs.getBoolean(i++);
      proj.accepted = rs.getBoolean(i++);
      proj.acceptedDate = rs.getDate(i++);
      proj.customerId = rs.getString(i++);
      proj.projectDescription = rs.getString(i++);
      proj.projectStatus = rs.getString(i++);
      list.add(proj);

    }
    return list;
  }
  ...
}

C�digo de ejemplo de la clase ValueListIterator:


package corepatterns.apps.psa.util;

import java.util.List;

public interface ValueListIterator {

  public int getSize()
    throws IteratorException;

  public Object getCurrentElement()
    throws IteratorException;

  public List getPreviousElements(int count)
    throws IteratorException;

  public List getNextElements(int count)
    throws IteratorException;

  public void resetIndex()
    throws IteratorException;

  // other common methods as required
  ...
}

.�Patrones Relacionados

  • Iterator [GoF]
    El patr�n Value List Handler est� basado en el patr�n Iterator, descrito en el libro de Gof: Design Patterns: Elements of Reusable Object-Oriented Software.
  • Session Facade
    Como el Value List Handler es un bean de sesi�n, podr�a aparecer como un Session Facade especializado. Sin embargo, aisladamente, es un bean de sesi�n especializado en vez de un Session Facade. Un Session Facade tiene otras motivaciones y caracter�sticas y es mucho m�s gen�rico.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR