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

Las aplicaciones cliente necesitan intercambiar datos con Beans Enterprise.

. Problema

Las aplicaciones de la plataforma J2EE implementan componentes de negocio del lado del servidor como beans de sesión y de entidad. Algunos métodos expuestos por los componentes de negocio devuelven datos al cliente. Algunas veces, el cliente invoca a los métodos get de un objeto de negocio varias veces para obtener todos los valores de los atributos.

Los beans de sesión representan los servicios de negocio y no se comparten entre usuarios. Un bean de sesión proporciona métodos de servicios genéricos cuando se implementan para el patrón Session Facade.

Por otro lado, los beans de entidad, son objetos transacionales, multiusuario que representan datos persistentes. Un bean de entidad expone los valores de los atributos proporcionando un método accesor (también referidos como métodos get) por cada atributo que desea exponer.

Toda llamada a método hecha al objeto de servicio de negocio, ya sea a un bean de entidad o a un bean de sesión, potencialmente es una llamada remota. Así, en una aplicación de JavaBeans Enterprise (EJB) dichas llamadas remotas usan la capa de red sin importar la proximidad del cliente al bean, creando una sobrecarga en la red. Las llamadas a métodos de beans enterprise podría penetrar las capas de red incluso si tanto el cliente como el contenedor EJB que mantiene el bean de entidad se están ejecutando en la misma JVM (Máquina Virtual Java), el mismo Sistema Operativo o máquina física. Algunos vendedores podrían implementar mecanismos para reducir esta sobrecarga utilizando una aproximación de acceso más directa pasando por encima de la red.

Según se incrementa la utilización de estos métodos remotos, el rendimiento de la aplicación se puede degradar significativametne. Por lo tanto, utilizar varias llamadas a métodos get que devuelven simples valores de atributos es ineficiente para obtener valores de datos desde un bean enterprise.

. Causas

  • Todos los accesos a un bean enterprise se realizan mediante interfaces remotos. Cada llamada a un bean enterprise potencialmente es una llamada a un método remoto con sobrecarga de red.
  • Normalmente, las aplicaciones tienen transaciones de lectura con mayor frecuencia que las de actualización. El cliente solicita los datos desde la capa de negocio para su presentación, y otros tipos de procesamientos de sólo-lectura. El cliente actualiza los datos de la capa de negocio con mucha menos frecuencia con la que los lee.
  • El cliente normalmente solicita valores que son algo más que un atributo o que son dependendientes del objeto de un bean enterprise. Así, el cliente podría invocar varias llamadas remotas para obtener los datos necesarios.
  • El número de llamadas que un cliente hace al bean enterprise impactan en el rendimiento de la red. Las aplicaciones charlatanas -- aquellas que incremenetan el trafico entre las capas del cliente y del servidor -- suelen degradar el rendimiento de la red.

. Solución

Utilizar un Transfer Object para encapsular los datos de negocio. Se utiliza una única llamada a un método para envíar y recuperar el Transfer Object. Cuando el cliente solicita los datos de negocio al bean enterprise, éste puede construir el Transfer Object, rellenarlo con sus valores de atributos y pasarlo por valor al cliente.

Los clientes normalmente solicitan más de un valor a un bean enterprise. Para reducir el número de llamadas remotas y evitar la sobrecarga asociada, es mejor el Transfer Objects para transportar los datos desde el bean enteprise al cliente.

Cuando un bean enterprise utiliza un Transfer Object, el cliente hace una sola llamada a un método remoto del bean enterprise para solicitar el Transfer Object en vez de numerosas llamadas remotas para obtener valores de atributos individuales. Entonces el bean enterprise construye un nuevo ejemplar Transfer Object, copia dentro los valores del objeto y lo devuelve al cliente. El cliente recibe el Transfer Object y puede entonces invocar los métodos accesores (o get) del Transfer Object para obtener los valores de atributos individuales del objeto Transfer Object. O, la implementación del Transfer Object podría hacer que todos los atributos fueran públicos. Como el Transfer Object se pasa por valor al cliente, todas las llamadas al ejemplar Transfer Object son llamadas locales en vez de invocaciones de métodos remotos.

. Estructura

Ls siguiente figura muestra el diagrama de clases que representa el patrón Transfer Object en su forma más simple:

Como se ve en este diagrama de clases, el Transfer Object lo construye bajo pedido el bean enterprise y lo devuelve al cliente remoto. Sin embargo, el patrón Transfer Object puede adoptar varias estrategias, dependendiendo de los requerimientos.

. Participantes y Responsabilidades

La siguiente figura contiene el diagrama de secuencia que muestra las interacciones del patrón Transfer Object:

. Client

Representa al cliente del bean enterprise. El cliente puede ser una aplicación final de usuario, como es el caso de una aplicación que ha sido diseñada para acceder directamente a beans enterprise. El cliente puede utilizar Business Delegate u otro BusinessObject diferente.

. BusinessObject

Representa un rol en este patrón que puede cumplir un bean de sesión, un bean de entidad, o un Data Access Object (DAO). BusinessObject es el responsable de crear el Transfer Object y devolverlo al cliente bajo pedido. El BusinessObject también podría recibir datos desde el cliente en la forma de un Transfer Object y utilizar esos datos para realizar una actualización.

. TransferObject

TransferObject es un objeto Java serializable referenciado como un Transfer Object. Una clase Transfer Object podría proporcionar un constructor que acepte todos los atributos requeridos para crear el Transfer Object. El constructor podría aceptar todos los valores de atributos del bean de entidad para el que se ha diseñado el Transfer Object. Normalmente, los miembros del Transfer Object se definen como públicos, así eliminan la necesidad de los métodos get y set. Si se necesita alguna protección, los miembros podrían definirse como protected o private, y se definirían métodos get para sus valores. Si no ofrece métodos set para los valores, un Transfer Object está protegido frente a modificaciones después de su creación. Si sólo se permite la modificación de unos pocos miembros para facilitar las actualizaciones, entonces si que se de deben proporcionar métodos set. Por lo tanto, la creacción del Transfer Object varía dependiendo de los requerimientos de la aplicación.

. Estrategias

Las dos primeras estrategias sen aplican cuando el bean enterprise se implementa como un bean de sesión o un bean de entidad.

Las estrategias posteriores son aplicables sólo cuando el BusinessObject se iplementa como un bean de entidad.

. Updatable Transfer Objects

En esta estrategia, el Transfer Object no sólo lleva los valores desde el BusinessObject hasta el cliente, también lleva los cambios realizados por el cliente de vuelta al objeto de negocio.

La siguiente figura es un diagrama de clases que muestra las relaciones entre el BusinessObject y el Transfer Object:

El BusinessObject crea el Transfer Object. Recuerda que un cliente podría necesitar aceder a los valores del BusinessObject no sólo para leerlos sino también para modificarlos. Para que el cliente pueda modificar los valores de los atributos del BusinessObject, éste debe proporcionar métodos mutadores. A éstos métodos también se les conoce como métodos set.

En lugar de proporcionar métodos set especificos para cada atributo, lo que resultaría en una sobrecarga de la red, el BusinessObject puede exponer un método setData() genérico que acepta un objeto Transfer Object como argumento. El Transfer Object pasado a este método contiene los valores actualizados por el cliente. Como el Transfer Object tiene que ser mutable, su clase tiene que proporcionar métodos set para todos los atributos que pueda modificar el cliente. Los métodos set del objeto Transfer Object pueden incluir validaciones a nivel de campo y chequeos de integridad si son necesarios. Una vez que el cliente obtiene un Transfer Object desde el BusinessObject, puede invocar localmente los métodos set necesarios para cambiar los valores de los atributos. Dichos cambios locales no impactan en el objeto BusinessObject hasta que se llame al método setData().

El método setData() serializa la copia que tiene el cliente del Transfer Object y la envía al BusinessObject. El BusinessObject recibe el Transfer Object modificado desde el cliente y mezcla los cambios con sus propios atributos. La operacion de mezcla podría complicar el diseño del BusinessObject y del Transfer Object; la sección sobre "Consecuencias" discute estas potenciales complicaciones. Una estrategia a utilizar aquí es actualizar sólo los atributos que han cambiado, en vez de actualizar todos los atributos. Una bandera de 'cambiado' situada en el Transfer Object se podría utilizar para determinar los atributos a actualizar, en vez de hacer una comparación directa.

Hay un impacto en el diseño cuando se usa esta estrategia en términos de propagación de actualización, sincronización y control de versión.

La siguiente figura muestra el diagram para toda la interacción de actualización:

. Multiple Transfer Objects

Algunos objetos de negocio de las aplicaciones pueden ser muy complejos. En dichos casos, es posible que un sólo objeto de negocio produzca diferentes Transfer Objects, dependiendo de la petición del cliente. Existe una relación uno-a-muchos entre el objeto de negocio y los muchos Transfer Objects que puede producir. En estas circunstancias debemos considerar la utilización de esta estrategia.

Por ejemplo, cuando el objeto de negocio se implementa como un bean de sesión, normalmente aplicando el patrón Session Facade, el bean podría interactúar con numerosos componentes de negocio para proporcionar el servicio. El bean de sesión produce su Transfer Object desde diferentes fuentes. De forma similar, cuando se implementa el BusinessObject como un bean de entidad genérico, normalmente aplicando el patrón Composite Entity, el bean de entidad tendrá relaciones complejas con varios objetos dependientes. En ambos casos, es una buena práctica proporcionar mecanismos para producir Transfer Objects que realmente representen a partes de los componentes genéricos.

Por ejemplo, en una aplicación tradicional, un Composite Entity que representa la cartera de un cliente puede ser un componente genérico muy complejo que puede producir Transfer Objects que proporcionen datos de las partes de la cartera, como información del cliente, lista de stocks, etc. Un ejemplo similar es un bean de sesión que maneja clientes y que proporciona servicios interactuando con otros muchos BusinessObjects y otros componentes para proporcionar su servicio. El bean controlador de clientes puede producir pequeños Transfer Objects, como direcciones de clientes, lista de contactos, etc., para representar partes de su modelo.

Para ámbos escenarios, es posible adoptar y aplicar la estrategia Multiple Transfer Objects para que los componentes de negocio, tanto si son beans de sesión como de entidad, puedan crear varios tipos de Transfer Objects. En esta estrategia, la entidad de negocio proporciona varios métodos para obtener diferentes Transfer Objects. Cada uno de esos métodos crea y devuelve un tipo diferente de Transfer Object. En la siguiente figura podemos ver el diagrama de clases para esta estrategia.

Cuando un cliente necesita un Transfer Object del tipo TransferObjectA, lama al método getDataA() de la entidad, pidiendo un TransferObjectA. Cuando necesita un Transfer Object del tipo TransferObjectB, llama al método getDataB() de la entidad, pidiendo un TransferObjectB, etc. Esto se puede ver en la siguiente figura:

. Entity Inherits Transfer Object

Cuando el BusinessObject se implementa como un bean de entidad y los clientes normalmente necesitan acceder a todos los datos del bean de entidad, entonces el bean de entidad y el Transfer Object tienen los mismos atributos. En este caso, como existe una relación uno-a-uno entre el bean de entidad y su Transfer Object, el bean de entidad podría ser capaz de utilizar la herencia para evitar la duplicación de código.

En esta estrategia, el bean de entidad extiende (o desdeciende de) la clase del Transfer Object. La única asumpción es que el bean de entidad y el Transfer Object comparten las mismas definiciones de atributos. En la siguiente figura podemos ver el diagrama de clases para esta estrategia:

El TransferObject implementa uno o más métodos getData() como explicamos en la estrategia anterior. Cuando el bean de entidad desciende de esta clase Transfer Object, el cliente invoca un método getData() heredado del bean de entidad para obtener un Transfer Object.

Así, esta estrategia elimina la duplicación de código entre el bean de entidad y el Transfer Object. También ayuda a manejar los cambios en los requerimientos del Transfer Object aislando los cambios de la clase Transfer Object y previniendo que los cambios afecten al bean de entidad.

Esta estrategia tiene un inconveniente relacionado con la herencia. Si el Transfer Object se comparte a través de su herencia, los cambios que se hagan en la clase Transfer Object afectarán a todas sus subclases, obligando potencialmente a modificar toda su descendencia. El diagrama de secuencia de la siguiente figura demuestra esta estrategia:

En la sección de Código de Ejemplo veremos esta implementación.

. Transfer Object Factory

La estrategia Entity Inherits Transfer Object se puede extender para que un bean de entidad soporte múltiples Transfer Objects empleando una factoría de Transfer Object para crear Transfer Objects bajo demanda utilizando reflection. Esto resulta en una estrategia aún más dinámica para la creación de Transfer Object.

Para conseguir esto, definimos un interface diferente para cada tipo de Transfer Object que se debe devolver. La implementación que hace el bean de entidad de la superclase de Transfer Object debe implementar todos esos interfaces. Además, debemos crear una clase separada para implementar todos los interfaces definidos, como se muestra en el diagrama de clases de la siguiente figura.

Una vez que se han definido e implementado todos los interfaces, creamos un método en la clase TransferObjectFactory al que se le pasan dos argumentos:

  • El ejemplar del bean de entidad para el que se debe crear el Transfer Object.
  • El interface que identifica el tipo de Transfer Object a crear.

Entonces el TransferObjectFactory puede ejemplarizar un objeto de la clase correcta, configurar sus valores, y devolver el ejemplar de Transfer Object recientemente creado.

En la siguiente figura podemos ver el diagrama de secuencia para esta estrategia:

El cliente le solicita un Transfer Object al BusinessEntity. Éste le pasa la clase del objeto Transfer Object requerido a TransferObjectFactory, que crea un nuevo Transfer Object de esa clase dada. El TransferObjectFactory usa reflection para obtener dinámicamente la información de clase para la clase del Transfer Object y construir un nuevo ejemplar. La obtención de los valores para el BusinessEntity que realiza el TransferObjectFactory se consigue utilizando invocación dinámica.

Estos son los beneficios de aplicar la estrategia Transfer Object Factory:

  • Se tiene que escribir menos código para crear Transfer Objects.
  • La misma factoría de clases Transfer Object puede ser reutilizada por diferentes beans enterprise.
  • Cuando cambia la definición de una clase Transfer Object, la factoría de objetos Transfer Object maneja estos cambios sin ningún esfuerzo de codificación adicional.
  • Esto incrementa la manejabilidad y es menos propenso a errores cuando se cambian las definiciones del Transfer Object.

La estrategia Transfer Object Factory tiene las siguienes consecuencias:

  • Esta basada en el hehco de que la implementación del bean enterprise extiende (desciende) de la clase general Transfer Object.
  • El Transfer Object general necesita implementar todos los interfaces definidos para diferentes Transfer Objects que se necesiten suministar al bean de entidad.
  • Se deben cumplir las convenciones de nombrado para poder hacer que esta estrategia funcione.
  • Como se utiliza reflection para inspeccionar y construir dinámicamente Transfer Objects, hay una ligera pérdida de rendimiento en su construcción.Sin embargo, si se considera el tiempo total de comunicación, dicha pérdida es mínima en su comparación.

Hay un inconveniente asociado con esta estrategia. Su poder y flexibilidad se deben sopesar en comparación con la sobrecarga de rendimiento asociada con la utilización de reflection en tiempo de ejecución.

. Consecuencias

  • Simplifica el Bean de Entidad y el Interface Remoto
    El bean de entidad proporciona un método getData() para obtener el Transfer Object que contiene los valores de los atributos. Esto podría eliminarse implementando múltiples métodos get en el bean y definiéndolos en el interface remoto del bean. De forma similar, si el bean de entidad proporciona un método setData() para actualizar los valores de atributos del bean de entidad con una sola llamada a método, se podría eliminar implementando varios métodos set en el bean.
  • Transfiere Más Datos en Menos Llamadas Remotas
    En lugar de realizar múltiples llamadas sobre la red al BusinessObject para obtener los valores de los atributos, esta solución proporciona una sola llamada a un método. Al mismo tiempo, esta única llamada obtiene una mayor cantidad de datos. Cuando consideremos la utilización de este patrón, debemos tener en cuenta el inconveniente dee el menor número de lamadas contra la mayor transmisión de datos por cada llamada. Alternativamente, podemos proporcionar ámbos tipos de métodos: métodos get y set específicos y métodos get y set genéricos. El desarrollador puede elegir la técnica apropiada según sus requerimentos.
  • Reduce el Tráfico de Red
    Un Transfer Object transfiere los valores desde el bean de entidad al cliente en una llamada a un método remoto. El Transfer Object actúa como un transportista de datos y reduce el número de llamadas remotas requeridas para obtener los valores de los atributos del bean; y esto significa un mejor rendimiento de la red.
  • Reduce la Duplicación de Código
    Usando las estrategias Entity Inherits Transfer Object y Transfer Object Factory, es posible reducir o eliminar la duplicación de código entre el bean de entidad y el Transfer Object. Sin embargo, con el uso de la estrategia Transfer Object Factory, se podría incrementar la complejidad de la implementación. También hay un coste asociado a la pérdida de rendimiento con esta estrategia al utilizar reflection dinámicamente. En la mayoría de los casos, la estrategia Entity Inherits Transfer Object podría ser suficiente para cumplir nuestras necesidades.
  • Podría Introducir Transfer Objects Obsoletos
    Adoptando la estrategia Updatable Transfer Objects se permite al cliente realizar modificaciones en la copia local del Transfer Object. Una vez que se han completado las modificaciones, el cliente puede invocar el método setData() del bean de entidad y pasarle el Transfer Object modificado. El bean recibe las modificaciones y las mezcla con los valores que él tiene. Sin embargo, podría haber un problema con Transfer Objects obsoletos. El bean de entidad actualiza sus valores, pero no tiene en cuenta si otros clientes han solicitado el mismo Transfer Object con anterioridad. Estos clientes podrían contener en su copia local ejemplares de Transfer Object que no reflejan las copia real de los datos del bean. Como el bean no tiene en cuenta a estos clientes, es posible propagar la actualización de Transfer Objects obsoletos que mantienen otros clientes.
  • Podría Incrementar la Complejidad Debido a la Sincronización y el Control de Versión
    El bean de entidad mezcla los valores modificados con sus propios valores cuando los recibe en un Transfer Object de un cliente. Sin embargo, el bean debe manejar la situación donde dos o más clientes simultáneamente solicitan actualizaciones conflictivas de los valores del bean. Permitir estas actualizaciones podría resultar en conflictos de datos. El control de versión es una forma de evitar estos conflictos. El bean de entidad puede incluir como uno de sus atributos un número de versión o una fecha de última-modificación. Este número de versión se copiaría en el Transfer Object. Una transación de actualización puede resolver conflictos utilizando el atributo del número de versión. Si un cliente que contiene un Transfer Object obsoleto intenta actualizar el bean, éste puede detectar el número de versión obsoleto e informar al cliente de esta condición de error. Entonces el cliente tiene que obtener el último Transfer Object y reintentar la actualización. En casos extremos esto podría resultar en un cliente hambriento -- el cliente nunca puede terminar su actualización.
  • Accesos y Transaciones Concurrentes
    Cuando dos o más clientes acceden de forma concurrente al BusinessObject, el contenedor aplica la semántica de transaciones de la arquitectura EJB. Si, para un bean entreprise, el nivel de aislamiento de transaciones se selecciona a TRANSACTION_SERIALIZED en el descriptor de despliegue, el contenedor proporciona la máxima protección a la transación y se asegura de su integridad. Por ejemplo, supongamos que el flujo de trabajo para la primera transación implica obtener un Transfer Object, luego el proceso contínua modificando el objeto BusinessObject. La segunda transación, como se han aislado las transaciones serializadas, obtendrá el Transfer Object con los valores correctos (los más actualizados). Sin embargo, para transaciones con menos restricciones, la protección es menos rígida, permitiendo inconsistencias en el Transfer Objects por accesos concurrentes. Además, tendremos que tratar con problemas relacionados con la sincronización, el control de versión y los Transfer Objects obsoletos.

. Código de Ejemplo

. Implementar el patrón Transfer Object

Consideremos un ejemplo donde se ha modelado un objeto de negocio llamado Project y se ha implementado como un bean de entidad. El bean de entidad Project necesita enviar datos a sus clientes en un Transfer Object cuando el cliente llame al método getProjectData(). Abajo podemos ver el código de la clase Transfer Object para este ejemplo, ProjectTO:

 
// Transfer Object to hold the details for Project
public class ProjectTO implements java.io.Serializable {
    public String projectId;
    public String projectName;
    public String managerId;
    public String customerId;
    public Date startDate;
    public Date endDate;
    public boolean started;
    public boolean completed;
    public boolean accepted;
    public Date acceptedDate;
    public String projectDescription;
    public String projectStatus;

    // Transfer Object constructors...
}
]>>
</codigo>

<para>
Abajo podemos ver el código para el bean de entidad de este ejemplo:
</para>
<codigo>
<![CDATA[
...
public class ProjectEntity implements EntityBean {
    private EntityContext context;
    public String projectId;
    public String projectName;
    public String managerId;
    public String customerId;
    public Date startDate;
    public Date endDate;
    public boolean started;
    public boolean completed;
    public boolean accepted;
    public Date acceptedDate;
    public String projectDescription;
    public String projectStatus;
    private boolean closed;

    // other attributes...
    private ArrayList commitments;
    ...

    // Method to get Transfer Object for Project data
    public ProjectTO getProjectData() {
      return createProjectTO();
    }

    // method to create a new Transfer Object and 
    // copy data from entity bean into the value 
    // object
    private ProjectTO createProjectTO() {
        ProjectTO proj = new ProjectTO();
        proj.projectId = projectId;
        proj.projectName = projectName;
        proj.managerId = managerId;
        proj.startDate = startDate;
        proj.endDate = endDate;
        proj.customerId = customerId;
        proj.projectDescription = projectDescription;
        proj.projectStatus = projectStatus;
        proj.started = started;
        proj.completed = completed;
        proj.accepted = accepted;
        proj.closed = closed;
        return proj;
    }
    ...
}

. Implementar la Estrategia Updatable Transfer Objects

El ejemplo anterior se puede ampliar para implementar la estrategia Updatable Transfer Objects. En este caso, el bean de entidad debería proporcionar un método setProjectData() para actualizar el bean de entidad pasándole un Transfer Object que contenga los datos utilizados para realizar la actualización. Abajo está el código para esta estrategia:

...
public class ProjectEntity implements EntityBean {
    private EntityContext context;
  ...
  // attributes and other methods as in Example 8.4
  ...

  // method to set entity values with a Transfer Object
  public void setProjectData(ProjectTO updatedProj) {
    mergeProjectData(updatedProj);
  }

  // method to merge values from the Transfer Object into
  // the entity bean attributes
  private void mergeProjectData(ProjectTO updatedProj) {
    // version control check may be necessary here 
    // before merging changes in order to 
    // prevent losing updates by other clients
    projectId = updatedProj.projectId;
    projectName = updatedProj.projectName;
    managerId = updatedProj.managerId;
    startDate = updatedProj.startDate;
    endDate = updatedProj.endDate;
    customerId = updatedProj.customerId;
    projectDescription                  = 
        updatedProj.projectDescription;
    projectStatus = updatedProj.projectStatus;
    started = updatedProj.started;
    completed = updatedProj.completed;
    accepted = updatedProj.accepted;
    closed = updatedProj.closed;
  }
  ...
}

. Implementar la Estrategia Multiple Transfer Objects

Consideremos un ejemplo donde los clientes acceden a un bean de entidad Resource para pedirle diferentes Transfer Objects. El primer tipo de Transfer Object, ResourceTO, se utiliza para transferir datos de un pequeño conjunto de atributos. El segundo tipo de Transfer Object, ResourceDetailsTO, se usa para transferir datos de un conjunto mayor de atributos. El cliente puede utilizar el primer Transfer Object si sólo necesita los datos básicos representados por ese Transfer Object, y puede utilizar el segundo si necesita información más detalla. Observa que esta estrategia se puede aplicar para producir dos o más Transfer Objects que contengan diferentes datos, y no sólo a un subconjunto o un superconjunto como se muestra aquí.

Código de ejemplo para el primer objeto Transfer Objects, ResourceTO:

// ResourceTO: This class holds basic information
// about the resource
public class ResourceTO implements 
  java.io.Serializable {
  public String resourceId;
  public String lastName;
  public String firstName;
  public String department;
  public String grade;
  ...
}

Código de ejemplo para el segundo objeto Transfer Objects, ResourceDetailsTO:

// ResourceDetailsTO This class holds detailed 
// information about resource
public class ResourceDetailsTO {
  public String resourceId;
  public String lastName;
  public String firstName;
  public String department;
  public String grade;
  // other data...
  public Collection commitments;
  public Collection blockoutTimes;
  public Collection skillSets;
}

Código del bean de entidad Resource:

// imports
...
public class ResourceEntity implements EntityBean {
  // entity bean attributes
  ...

  // entity bean business methods
  ...

  // Multiple Transfer Object method : Get ResourceTO
  public ResourceTO getResourceData() {

    // create new ResourceTO instance and copy
    // attribute values from entity bean into TO
    ...
    return createResourceTO();
  }

  // Multiple Transfer Object method : Get 
  // ResourceDetailsTO
  public ResourceDetailsTO getResourceDetailsData() {

    // create new ResourceDetailsTO instance and copy
    // attribute values from entity bean into TO
    ...
    return createResourceDetailsTO(); 
  }

  // other entity bean methods
  ...
}

Código de Ejemplo del cliente del bean de entidad:

 

...
  private ResourceEntity resourceEntity;
  private static final Class homeClazz =

  corepatterns.apps.psa.ejb.ResourceEntityHome.class;
  ...
  try {
    ResourceEntityHome home =
      (ResourceEntityHome)
        ServiceLocator.getInstance().getHome(
            "Resource", homeClazz);
        resourceEntity = home.findByPrimaryKey( 
                            resourceId);
  } catch(ServiceLocatorException ex) {
    // Translate Service Locator exception into
    // application exception
    throw new ResourceException(...);
  } catch(FinderException ex) {
    // Translate the entity bean finder exception into
    // application exception
    throw new ResourceException(...);
  } catch(RemoteException ex) {
    // Translate the Remote exception into
    // application exception
    throw new ResourceException(...);
  }
  ...
  // retrieve basic Resource data 
  ResourceTO vo = resourceEntity.getResourceData();
  ...
  // retrieve detailed Resource data
  ResourceDetailsTO =           
    resourceEntity.getResourceDetailsData();
  ...

. Implementar la Estrategia Entity Inherits Transfer Object

Consideremos un ejemplo donde un bean de entidad, ContactEntity hereda todas sus propiedades de un Transfer Object ContactTO. El siguiente ejemplo muestra el codigo para el Transfer Object ContactTO que ilustra esta estrategia:

// This is the Transfer Object class inherited by
// the entity bean
public class ContactTO 
  implements java.io.Serializable { 

  // public members
  public String firstName;
  public String lastName; 
  public String address;

  // default constructor
  public ContactTO() {}

  // constructor accepting all values
  public ContactTO(String firstName, 
    String lastName, String address){
      init(firstName, lastName, address);
  }

  // constructor to create a new TO based 
  // using an existing TO instance
  public ContactTO(ContactTO contact) {
    init (contact.firstName, 
      contact.lastName, contact.address);
  }

  // method to set all the values
  public void init(String firstName, String 
              lastName, String address) { 
    this.firstName = firstName; 
    this.lastName = lastName; 
    this.address = address;
  } 

  // create a new Transfer Object 
  public ContactTO getData() { 
    return new ContactTO(this);
  } 
}

En el siguiente ejemplo podemos ver el código más importante del bean de entidad para esta estrategia:

public class ContactEntity extends ContactTO implements 
javax.ejb.EntityBean { 
  ...
  // the client calls the getData method
  // on the ContactEntity bean instance.
  // getData() is inherited from the Transfer Object
  // and returns the ContactTO Transfer Object
  ...
}

. Implementar la Estrategia Transfer Object Factory

El siguiente fragmento de código demuestra la estrategia Transfer Object Factory. El bean de entidad extiende un Transfer Object general llamado CustomerContactTO. El CustomerContactTO implementa dos interfaces, Customer y Contact. El Transfer Object CustomerTO implementa Customer, y el Transfer Object ContactTO implementa Contact.

public interface Contact 
  extends java.io.Serializable {
  public String getFirstName();
  public String getLastName();
  public String getContactAddress();
  public void setFirstName(String firstName);
  public void setLastName(String lastName);
  public void setContactAddress(String address);
}

public class ContactTO implements Contact {
  // member attributes
  public String firstName;
  public String lastName;
  public String contactAddress;

  // implement get and set methods per the 
  // Contact interface here. 
  ...
        }
public interface Customer 
  extends java.io.Serializable {
  public String getCustomerName();
  public String getCustomerAddress();
  public void setCustomerName(String customerName);
  public void setCustomerAddress(String 
      customerAddress);
}

public class CustomerTO implements Customer {
  public String customerName;
  public String customerAddress;

  // implement get and set methods per the 
  // Customer interface here.
  ...
}

public class CustomerContactTO implements Customer, 
  Contact {
  public String firstName;
  public String lastName;
  public String contactAddress;
  public String customerName;
  public String customerAddress;

  // implement get and set methods per the 
  // Customer and Contact interfaces here.
  ...
}

En el siguiente codigo tenemos el bean de entidad para obtener estos tres Transfer Objects diferentes:

public class CustomerContactEntity extends 
  CustomerContactTO implements javax.ejb.EntityBean {

  // implement other entity bean methods...not shown

  // define constant to hold class name
  // complete Transfer Object. This is required by
  // the TransferObjectFactory.createTransferObject(...)
  public static final String COMPLETE_TO_CLASSNAME =
      "CustomerContactTO";

  // method to return CustomerContactTO Transfer Object
  public CustomerContactTO getCustomerContact() {
    return (CustomerContactTO)
      TransferObjectFactory.createTransferObject(
        this, "CustomerContactTO",
        COMPLETE_TO_CLASSNAME);
  }

// method to return CustomerTO Transfer Object
  public CustomerTO getCustomer() {
    return (CustomerTO)
      TransferObjectFactory.createTransferObject(
        this, "CustomerTO",
        COMPLETE_TO_CLASSNAME);
  }

  // method to return ContactTO Transfer Object
  public ContactTO getContact() {
    return (ContactTO)
      TransferObjectFactory.createTransferObject(
        this, "ContactTO",
        COMPLETE_TO_CLASSNAME);
  }

  // other entity bean business methods
  ...
}

Abajo tenemos el código de la clase TransferObjectFactory:

import java.util.HashMap;
import java.lang.*;

/**
* The factory class that creates a Transfer Object for a
* given EJB.
*/
public class TransferObjectFactory {

/**
* Use a HashMap to cache class information for
* Transfer Object classes
*/
private static HashMap classDataInfo = new HashMap();

/**
* Create a Transfer Object for the given object. The
* given object must be an EJB Implementation and have
* a superclass that acts as the class for the entity's
* Transfer Object. Only the fields defined in this
* superclass are copied in to the Transfer Object.
*/
public static java.io.Serializable
  createTransferObject(Object ejb, 
    String whichTOType,
    String completeTOType) {
      try {
      // Get the class data for the complete 
      // Transfer Object type 
      ClassData cData = getClassData (completeTOType); 

      // Get class data for the requested TO type
      ClassData voCData = getClassData (whichTOType);

      // Create the Transfer Object of the requested 
      // Transfer Object type...
      java.lang.Object whichTO =       
          Class.forName(whichTOType).newInstance();

      // get the TO fields for the requested TO
      // from the ClassData for the requested TO
      java.lang.reflect.Field[] voFields = 
                  voCData.arrFields;

      // get all fields for the complete TO
      // from the ClassData for complete TO
      java.lang.reflect.Field[] beanFields = 
                  cData.arrFields;

      // copy the common fields from the complete TO 
      // to the fields of the requested TO
      for (int i = 0; i < voFields.length; i++) {
        try {
          String voFieldName = voFields[i].getName();
          for (int j=0; j < beanFields.length; j++) {
            // if the field names are same, copy value
            if ( voFieldName.equals( 
                  beanFields[j].getName())) {
              // Copy value from matching field
              // from the bean instance into the new
              // Transfer Object created earlier
              voFields[i].set(whichTO, 
                    beanFields[j].get(ejb));
              break;
            }
          }
        } catch (Exception e) {
          // handle exceptions that may be thrown
          // by the reflection methods...
        }
      }
    // return the requested Transfer Object
    return (java.io.Serializable)whichTO;
  } catch (Exception ex) { 
    // Handle all exceptions here...
  }
  return null;
}

/**
* Return a ClassData object that contains the 
* information needed to create
* a Transfer Object for the given class. This information
* is only obtained from the
* class using reflection once, after that it will be 
* obtained from the classDataInfo HashMap.
*/
private static ClassData getClassData(String 
  className){

  ClassData cData = 
    (ClassData)classDataInfo.get(className);

  try {
    if (cData == null) {
      // Get the class of the given object and the 
      // Transfer Object to be created
      java.lang.reflect.Field[] arrFields ;
      java.lang.Class ejbTOClass = 
          Class.forName(className);

      // Determine the fields that must be copied
      arrFields = ejbTOClass.getDeclaredFields();

      cData = new ClassData(ejbTOClass, arrFields);
      classDataInfo.put(className, cData);
    }
  } catch (Exception e) {
    // handle exceptions here...
  }
  return cData;
  }
}

/**
* Inner Class that contains class data for the
* Transfer Object classes
*/
class ClassData {
  // Transfer Object Class
  public Class    clsTransferObject;

  // Transfer Object fields
  public java.lang.reflect.Field[] arrFields;

  // Constructor
  public ClassData(Class cls, 
      java.lang.reflect.Field[] fields) {
    clsTransferObject = cls;
    arrFields = fields;
  }
}

. Patrones Relacionados

  • Session Facade
    Session Facade, que es el interface de negocio para clientes de aplicaciones J2EE, frecuentemente utiliza Transfer Objects como mecanismo de intercambio con beans de entidad. Cuando la fachada actúa como un proxy para los servicios de negocio, el Transfer Object obtenido de los beans de entidad se puede pasar al cliente.
  • Transfer Object Assembler
    Transfer Object Assembler es un patrón que construye Transfer Objects compuestos desde diferentes fuentes de datos. Las fuentes de datos suelen ser beans de sesión o de entidad a los que se podría solicitar que proporcionaran sus datos al Transfer Object Assembler como Transfer Objects. Estos objetos se considerarán parte del objeto compuesto que ensambla el Transfer Object Assembler.
  • Value List Handler
    Value List Handler es otro patrón que proporciona una lista de Transfer Objects construidos dinámicamente accediendo al almacenamiento persistente en el momento de la solicitud.
  • Composite Entity
    El patrón Transfer Object corrige la necesidad de obtener los datos de BusinessObjects a través de las capas. Ciertamente este es uno de los aspectos de consideración de diseño para los beans de entidad. El patrón Composite Entity explica los problemas implicados en el diseño de beans de entidad genéricos. Este patrón corrige los requerimientos complejos y explica los factores y consideraciones implicados en el diseño de beans de entidad.

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
ARTÍCULO ANTERIOR

¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.