Zona HTML Zona Java Zona PHP Zona ASP Zona Bases de datos
Inicio > Tutoriales > Lenguajes orientados a objeto > Java > J2EE > Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación
-Tutoriales

Catálogo de Patrones de Diseño J2EE. I.- Capa de Presentación


Service to Worker

. Contexto

El sistema controla el flujo de ejecución y accede a los datos de negocio, desde los que crea el contenido de la presentación.

Nota:
El patrón Service to Worker, igual que el patrón Dispatcher View, describe una combinación común de otros patrones del catálogo. Estos dos macro-patrones describen las combinación de un controlador y un dispatcher con vistas y helpers. Aunque describen esta estructura común, cada uno enfatiza un uso diferentes de los patrones.

. Problema

El problema es una combinación de los problemas resueltos por los patrones Front Controller y View Helper de la capa de presentación. No hay un componente centralizado para manejar el control de acceso, la recuperación de contenido, o el manejo de la vista, y hay código de control duplicado esparcido por varias vistas. Además, la lógica de negocio y la de formateo de la presentación están mezcladas en esas vistas, haciendo que el sistema sea menos flexible, menos reutilizables y generalmente menos resistente a los cambios.

Mezclar lógica de negocio con el procesamiento de la vista también reduce la modularidad y proporciona una pobre separación de roles entre los equipos de producción Web y de desarrollo de software.

. Causas

  • Los chequeos de autentificación y autorización se completan en cada petición.
  • El código scriptlet dentro de las vistas se debería minimizar.
  • La lógica de negocio se debería encapsular en componentes distintos a la vista.
  • El control de flujo es relativamente complejo y se basa en valores del contenido dinámico.
  • La lógica de control de la vista es relativamente sofisticada, con varias vistas que potencialmente se mapean a la misma petición.

. Solución

Combinar un controlador y un dispacher con vistas y helper (ver Front Controller y View Helper) para manejar peticiones de clientes y preparar una presentación dinámica como respuesta. Los controladores delegan la recuperación de contenido en los helpers, que manejan el relleno del modelo intermedio para la vista. Un dispatcher es el responsable del control de la vista y la navegación y puede encapsularse dentro de un controlador o de un componente separado.

Service to Worker describe la combinación de los patrones Front Controller y View Helper con un componente dispatcher.

Aunque este patrón y Dispatcher View describen una estructura similar, ambos sugieren diferentes divisiones de la labor entre los componentes. En Service to Worker, el controlador y el dispatcher tienen más responsabilidades.

Aunque los patrones Service to Worker y Dispatcher View representan una combinación de otros patrones del catálogo, el primero garantiza con su nombre una comunicación eficiente entre los desarrolladores. Mientras que el segundo sugiere una recuperación de contenido relegada al momento de procesamiento de la vista.

En el patrón Dispatcher View, el dispatcher normalmente juega un rol moderado en el control de la vista. En el patrón Service to Worker, el dispatcher juega un rol algo más elevado en el control de la vista.

Un rol limitado para el dispatcher ocurre cuando no se utiliza recursos exteriores para poder elegir la vista. La información encapsulada en la petición es suficiente para determinar la vista a la que despachar la petición. Por ejemplo:

http://some.server.com/servlet/Controller?next=login.jsp

La única responsabilidad del componente dispatcher en este caso es reenviar a la vista login.jsp.

Un ejemplo del dispatcher jugando un rol moderado es el caso donde el cliente envía una petición directamente al controlador con un parámetro de consulta que describe una acción a realizar:

http://some.server.com/servlet/Controller?action=login

Aquí la responsabilidad del dispatcher es traducir el nombre lógico login en el nombre del recurso de una vista apropiada, como login.jsp, y reenviar a esa vista. Para conseguir esta traducción, el dispatcher podría acceder a recursos como un fichero de configuración XML que especifica las vistas apropiadas a mostrar.

Por otro lado, en el patrón Service to Worker, el dispatcher podría invocar servicios de negocio para determinar la vista apropiada que se debe mostrar.

La estructrua compartida de Service to Worker y Dispatcher View consiste en un controlador trabajanado con un dispatcher, vistas y helpers.

. Estructura

En la siguiente figura podemos ver el diagrama de clases que representa al patrón Service to Worker.

. Participantes y Responsabilidades

La siguiente figura muestra el diagrama de secuencia que representa al patrón Service to Worker.

Como hemos comentado, Service to Worker y Dispatcher View representan una estructura similar. La principal diferencia es que Service to Worker describe arquitecturas con un comportamiento más up front (más cercano) al controlador y al dispatcher, mientras que Dispatcher View describe arquitecturas donde se ha movido más comportamiento al momento del procesamiento de la vista. Así, los patrones sugieren una continuidad, donde el comportamiento se ha encapsulado más cerca de la vista o se ha movido hacía atrás en el flujo de proceso.

. Controller

El controlador normalmente es el punto de contacto inicial para manejar una petición. Funciona con un dispatcher para completar el control de la vista y la navegación. El controlador maneja la autentificación, la autorización, la recuperación de contenido, la validación y otros aspectos del manejo de la petición. Delega en los helpers para completar partes de este trabajo.

. Dispatcher

Un dispatcher es el responsable del control de la vista y la navegación, controlando la elección de la siguiente vista a mostrar y proporciona el mecanismo para dirigir el control a este recurso.

Un dispatcher se puede encapsular dentro de un controlador (ver Front Controller) o puede ser un componente independiente que trabaja en coordinación con el controlador. El dispatcher puede proporcionar reenvío estático a la vista o podría proporcionar un mecanismo de reenvío dinámico más sofisticado.

El dispatcher utiliza el objeto RequestDispatcher (soportado en la especificación Servlet), pero también encapsula alguna información de procesamiento adicional. Cuantas más responsabilidades encapsule este componente, más se acercará al ideal del patrón Service to Worker. Y al controlario, cuando el diapatcher juega un papel más limitado, más se acercará al ideal del patrón Dispatcher View.

. View

Una Vista representa una presentación de información al cliente. La información utilizada en esta presentación se recupera de un modelo. Los helpers soportan vistas encapsulando y adaptando un modelo para utilizarlo en una presentación.

. Helper

Un helper es el responsable de ayudar a la vista o al controlador a completar su procesamiento. Así, los helpers tienen numerosas responsabilidades, incluyendo la obtención de los datos requeridos por la vista y almacenándolos en el modelo intermedio, en cuyo caso el helper es conocido como un value bean. Además, los helpers podrían adaptar este modelo de datos para que los utilice la vista. Los helpers pueden servir peticiones de datos desde la vista simplemente proporcionando acceso a los datos en bruto o formateándolos como contenido Web.

Una vista podría trabajar con cualquier número de helpers, que normalmente están implementados por componentes JavaBeans (JSP 1.0+) o componentes de etiquetas personalidas (JSP 1.1+). Además, un helper podría representar un objeto Command o delegate.

. ValueBean

Un value bean es otro nombre para un helper que es responsable de contener el estado del modelo intermedio para que lo utilice una vista.

. BusinessService

Servicio de Negocio es un rol que cumple el servicio al que el cliente quiere acceder. Normalmente, se accede al servicio de negocio mediante un Business delegate. El rol del business delegate es proporcionar control y protección para el servicio de negocio (puedes ver el patrón "Business Delegate" más adelante).

. Estrategias

. Servlet Front

Ver la estrategia "Servlet Front" en el patrón Front Controller.

. JSP Front

Ver la estrategia "JSP Front" en el patrón Front Controller.

. JSP page View

Ver la estrategia "JSP page View" en el patrón View Helper.

. Servlet View

Ver la estrategia "Servlet View" en el patrón View Helper.

. JavaBean Helper

Ver la estrategia "JavaBean Helper" en el patrón View Helper.

. Custom Tag Helper

Ver la estrategia "Custom Tag Helper" en el patrón View Helper.

. Dispatcher in Controller

Ver la estrategia "Dispatcher in Controller" en el patrón Front Controller.

Como hemos visto, los patrones Service to Worker y Dispatcher View sugieren una continuidad, donde el comportamiento se ha encapsulado más cerca de la vista o se ha movido hacía atrás en el flujo de proceso. La siguiente figura describe un escenario en el que se ha cargado al controlador con mucho trabajo, pero la funcionalidad del dispatcher es mínima.

. Transformer Helper

Ver la estrategia "Transformer Helper" en el patrón View Helper.

. Consecuencias

  • Centraliza el Control y Mejora la Modularidad y la Reutilización
    Este patrón sugiere proporcionar un lugar central para manejar los servicios del sistema y la lógica de negocio entre varias peticiones. El contolador maneja el procesamiento de la lógica de negocio y el manejo de peticiones. Hay que tener en cuenta, que como control centralizado, es posible introducir un sólo unto de fallo.

    El patrón también promueve el particionamiento de la aplicación y aconseja la reutilización. El código común se mueve dentro de un controlador y es reutilizado por las peticiones y movido dentro de componentes helpers, en los que delegan los controladores y las vistas. La mejora de modularidad y de reutilización significa menos duplicación de código, que normalmente significa en entorno más libre de bugs.

  • Mejora el Particionamiento de la Aplicación
    La utilización de helpers resulta en una separación clara entre la vista y el procesamiento de negocio en una aplicación. Los helpers, en la forma de JavaBeans (JSP 1.0+) o etiquetas personalizadas (JSP 1.1+), proporcionan un lugar donde construir la lógica de negocio fuera de la página JSP. Si la lógica de negocio se deja dentro de la página JSP, los grandes proyectos resultan embrollados.
  • Mejora la Separación de Roles
    Al separar la lógica de formateo de la lógica de negocio de la aplicación también se reducen las dependencias de los mismos recursos entre individuos que cumplen diferentes roles. Sin esta separación, por ejemplo, un desarrollador de sofware poseería código que está embebido dentro de marcas HTML, mientras que un miembro del equipo de producción Web necesitaría modificar la distribución de una página y diseñar componentes que están mezclados con lógica de negocio. Como ningún individuo que cumple estos roles está familiarizado con las implementaciones específicas del trabajo del otro individuo, se puede llegar a un punto de confusión en que las modificaciones accidentales introduzcan errores el sistema.

. Código de Ejemplo

Los siguientes fragmentos de códigos muestran una implementación del patrón Service to Worker, utilizando un servlet controlador, un helper command, un componente dispatcher y una vista. La implementación incluye las estrategias Servlet Front, Command and Controller, JSP View y JavaBean Helper. También se utiliza una vista compuesta muy básica, como se puede ver en la siguiente imagen:

El siguiente ejemplo muestra el controlador servlet, que delega en un objeto Command para completar el procesamiento del control. El objeto Command se recupera mediante una llamada a la factoría, que devuelve un Command de tipo genérico, como veremos más adelante. El ejemplo utiliza un LogManager para guardar mensajes.


public class Controller extends HttpServlet {
  /** Processes requests for both HTTP  
   * <code>GET</code> and <code>POST</code> methods.
   * @param request servlet request
   * @param response servlet response
   */
  protected void processRequest(HttpServletRequest 
    request, HttpServletResponse response)
    throws ServletException, java.io.IOException {
    String next;

    try {
      // Log pattern info
      LogManager.recordStrategy(request,                                
        "Service To Worker", 
              " ServletFront Strategy;" + 
      " JSPView Strategy; JavaBean helper Strategy");

      LogManager.logMessage(request, getSignature(), 
        "Process incoming request. ");

      // Use a helper object to gather parameter 
      // specific information.
      RequestHelper helper = new 
        RequestHelper(request, response);

      LogManager.logMessage(request, getSignature(), 
          "Getting command object helper");

      // Get command object helper
      Command command = helper.getCommand();
      // delegate processing to the command object, 
      // passing request and response objects along
      next = command.execute(helper);

      /** If the above command returns a value, we 
        * will dispatch from the controller. In this 
        * example, though, the command will use a 
        * separate dispatcher component to choose a 
        * view and dispatch to that view. The command 
        * object delegates to this dispatcher 
        * component in its execute method, above, and 
        * control should not return to this point **/
    }
    catch (Exception e) {
      LogManager.logMessage( 
        "EmployeeController(CommandStrategy)",
        e.getMessage() );

      /** ApplicationResources provides a simple API 
        * for retrieving constants and other 
        * preconfigured values**/
      next = ApplicationResources.getInstance(). 
                    getErrorPage(e);
    }

    dispatch(request, response, next);

  }

  /** Handles the HTTP <code>GET</code> method.
   * @param request servlet request
   * @param response servlet response
   */
  protected void doGet(HttpServletRequest request, 
    HttpServletResponse response)
    throws ServletException, java.io.IOException {
      processRequest(request, response);
  }

  /** Handles the HTTP <code>POST</code> method.
   * @param request servlet request
   * @param response servlet response
   */
  protected void doPost(HttpServletRequest request, 
    HttpServletResponse response)
  throws ServletException, java.io.IOException {
    processRequest(request, response);
  }

  /** Returns a short description of the servlet. */
  public String getServletInfo() {
    return getSignature();
  }

  /** dispatcher method */
  protected void dispatch(HttpServletRequest request, 
    HttpServletResponse response,
    String page) throws 
  javax.servlet.ServletException, 
    java.io.IOException {
      RequestDispatcher dispatcher = 
       getServletContext().getRequestDispatcher(page);
      dispatcher.forward(request, response);
  }

  public void init(ServletConfig config) throws 
      ServletException {
    super.init(config);
  }

  public void destroy() { }

  private String getSignature() {
    return "ServiceToWorker-Controller";
  }
}

En el siguiente fragmento tenemos el interface Command:


public interface Command {

    public String execute(RequestHelper helper) throws
  javax.servlet.ServletException, java.io.IOException;
}

Todo objeto command implementa este interface genérico, que es un ejemplo del patrón Command de GoF. El objeto command es un ejemplar de la clase ViewAccountDetails, que podemos ver abajo. El ejemplar de command delega en un AccountingAdapter para hacer una llamada a la capa de negocio mediante Business Delegate.


public class ViewAccountDetailsCommand implements 
  Command {
  public ViewAccountDetailsCommand() { }

  // view account details operation
  public String execute(RequestHelper helper)
    throws javax.servlet.ServletException, 
  java.io.IOException {
    /** This will tell the user that a system error 
      * has occured and will typically not be seen. It 
      * should be stored in a resource file **/
    String systemerror = 
      "/jspdefaultprocessingerror.jsp";
        
    LogManager.logMessage(helper.getRequest(), 
      "ViewAccountDetailsCommand", 
      "Get Account Details from an adapter object");

    /** Use an adapter to retrieve data from business 
      * service, and store it in a request attribute.
      * Note: Object creation could be avoided via 
      * factory, but for example purposes object 
      * instantiation is shown **/
    AccountingAdapter adapter = new 
                           AccountingAdapter();
    adapter.setAccountInfo(helper);

    LogManager.logMessage(helper.getRequest(), 
  "ViewAccountDetailsCommand", "processing complete");

    /** Note: Object creation could be avoided via 
      * factory, but for example purposes object 
      * instantiation is shown**/
    Dispatcher dispatcher = new Dispatcher();
    dispatcher.dispatch(helper);
   
    /** This return string will not be sent in a 
      * normal execution of this scenario, because 
      * control is forwarded to another resource 
      * before reaching  this point. Some commands do 
      * return a String,  though, so the return value 
      * is included for  correctness. **/
    return systemerror;
  }
}

La clase adaptador, mostrada en el siguiente ejemplo, utiliza una componente dispatcher independiente para determinar la siguiente vista a la que se debería reenviar el control y para reenviar realmente el control a esa vista.


public class AccountingAdapter {
    public void setAccountInfo(
      RequestHelper requestHelper) {
        LogManager.logMessage(
          requestHelper.getRequest(), 
          "Retrieving data from business tier");

        // retrieve data from business tier via 
        // delegate. Omit try/catch block for brevity.
        AccountDelegate delegate = 
                new AccountDelegate();
        AccountTO account =     
          delegate.getAccount( 
            requestHelper.getCustomerId(),
            requestHelper.getAccountKey());

        LogManager.logMessage( 
          requestHelper.getRequest(), 
  "Store account Transfer Object in request attribute");

      // transport data using request object                                    
      requestHelper.getRequest().setAttribute(
         "account", account);
    }
}

La invocación del servicio de negocio mediante el delegado tiene que ver con un objeto Account Transfer, que el adaptador almacena en un atributo de la petición para utilizarlo en la vista. El siguiente ejemplo muestra accountdetails.jsp, la página JSP que despachará la petición. El objeto Transfer Object se importa mediante la etiqueta estándar <jsp:useBean> y se accede a sus propiedades utilizando la etiqueta estándar <jsp:getProperty>. La vista también utiliza una estrategia Composite muy simple, haciendo la inclusión de la subvista trace.jsp durante la traducción, está subvista es la responsable de guardar información de la presentación sólo para propósitos de ejemplo.


<html>
<head><title>AccountDetails</title></head>
<body>

<jsp:useBean id="account" scope="request"
  class="corepatterns.util.AccountTO" /> 

<h2><center> Account Detail for <jsp:getProperty 
  name="account" property="owner" />
</h2> <br><br>
<table border=3>
<tr>
<td>
Account Number :
</td>
<td>
<jsp:getProperty name "account" property="number" />
</td>
</tr>

<tr>
<td>
Account Type:
</td>
<td>
<jsp:getProperty name="account" property="type" />
</td>
</tr>

<tr>
<td>
Account Balance:
</td>

<td>
<jsp:getProperty name="account" property="balance" />
</td>
</tr>

<tr>
<td>
OverDraft Limit:
</td>
<td>
<jsp:getProperty name="account" 
  property="overdraftLimit" />
</td>
</tr>

</table>

<br>
<br>

</center>
<%@ include file="/jsp/trace.jsp" %>
</body>
</html>

. Patrones Relacionados

  • Front Controller y View Helper
    El patrón Service to Worker es el resultado de combinar el patrón View Helper con un dispatcher, en coordinación con el patrón Front Controller.
  • Dispatcher View
    El patrón Dispatcher View es otro nombre para la combinación del patrón Front Controller con un dispatcher, y el patrón View Helper. Los patrones Service to Worker y Dispatcher View son idénticos con respecto a los componentes implicados, pero son diferentes en la división de labores entre esos componentes. El patrón Dispatcher View siguiere relegar la recuperación de contenido al momento en que se procesa la vista. Además, el dispatcher juega un rol más limitado en el control de la vista, ya que la elección de le vista normalmente ya está incluida en la petición.
 
Patrocinados
 

Copyright © 1999-2006 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad

Hospedaje web y servidores dedicados linux por Ferca Network