Diseño de aplicaciones Internet usando los Patrones de diseño J2EE

© Copyright Moisés Daniel Díaz.

Comúnmente se construyen aplicaciones web en las que cada página jsp gestiona servicios de seguridad, de recuperación de contenidos, y la navegación. Esto nos lleva a un modelo con un alto coste de mantenimiento, en el que tenemos grandes cantidades de código duplicado en numerosas Jsp (típica solución que se desarrolla usando la conocida técnica de copiar-pegar y modificar un poco).

Podemos mejorar enormemente la calidad de estas aplicaciones centralizando y encapsulando algunos de estos mecanismos haciendo la aplicación mucho más mantenible, sencilla, y limpia, al eliminar gran cantidad de scriptlets (código java embebido en las páginas JSPs). Para la consecución de estos objetivos no hay nada mejor que la experiencia condensada de muchos años de desarrollo y diseño: los patrones de diseño J2EE.

Patrones de diseño

El concepto de Patrón

Podemos decir que desde que hizo su aparición la orientación a objetos, el descubrimiento y uso de los patrones de diseño ha sido una de las principales innovaciones en la creación de software.

Recordemos que el desarrollo orientado a objetos debe su éxito al incremento de la calidad del software escrito bajo este paradigma, haciéndolo más flexible, modular, reutilizable y comprensible. Resumiendo, elevando tanto su calidad interna como externa (ambas muy relacionadas).

Las clases y los objetos nos dieron nuevos medios para encapsular abstracciones, dividir la realidad, y disminuir la complejidad. También nos han permitido basar nuestros esfuerzos en conceptos inherentes del problema a resolver de una forma más explícita.

En este contexto, ¿ qué es lo que nos han proporcionado los patrones de diseño? Estos elementos nos han dado la posibilidad de organizar nuestras clases en estructuras comunes y bien probadas; modificando el sistema para mejorar su flexibilidad y extensibilidad. En otras palabras, incrementando la facilidad para adaptar el software a los cambios de especificación e incrementando su posible reutilización. De alguna forma, los mismos beneficios producidos por la programación orientada a objetos pero mejorando aún más la calidad del diseño, y por lo tanto el software en sí.

La noción de patrón de diseño existía con bastante anterioridad a su uso común en la comunidad informática. Es comúnmente aceptado que el trabajo que originó esta disciplina en la forma actualmente entendida, fue el libro “Design Patterns: Elements of Reusable Object-Oriented Software” escrito por Gamma, Helm, Jonson, Vlissides, y conocido generalmente como ‘GOF’.

Este libro propone la siguiente definición del concepto de patrón de diseño: “Los patrones de diseño son descripciones de objetos y clases comunicándose, que son adaptadas para resolver un problema de diseño general en un contexto particular”. Por lo tanto, los patrones aparecen de esta forma centrados en la micro-estructura de las aplicaciones, es decir, sus clases y objetos.

Si miramos el catálogo de patrones del GOF podemos observar que muchos de ellos tienen una estructura semejante y producen un impacto similar en el sistema. Para tener una visión clara sobre las relaciones entre ellos, ya tenemos que irnos al concepto de metapatrón.

Quedándonos en los patrones, decir que es frecuente que se estructuren en ‘Lenguajes de Patrones’, pudiéndose definir estos como : “La especificación de una serie de patrone (elementos y sus relaciones) de tal forma que nos permiten describir buenas soluciones a diferentes problemas que aparecen en un contexto específico”.

El principal objetivo de los patrones de diseño es capturar buenas prácticas que nos permitan mejorar la calidad del diseño de sistemas, determinando objetos que soporten roles útiles en un contexto específico, encapsulando complejidad, y haciéndolo más flexible.

Después de hablar tanto sobre patrones, aquí tenemos uno para afianzar mejor los conceptos: Patrón: Estrategia.

  • Intención: Definir una familia de algoritmos, encapsulando cada uno y haciéndolos intercambiables.
  • Aplicabilidad: Usar cuando...
    • Son necesitadas diferentes variantes de un mismo algoritmo.
    • Varias subclases sólo difieran en su comportamiento.
    • Una clase define varios comportamientos en múltiples sentencias condicionales.
  • Estructura: Inicialmente tenemos un algoritmo encapsulado en la clase ‘ClassXYZ’, pero nos damos cuenta que necesitamos hacer nuestro diseño más flexible porque requerimos tener la posibilidad de aplicar diferentes algoritmos en tiempo de ejecución.
Estructura

La solución consiste en sacar (desacoplar) el algoritmo que tenemos encapsulado en la ‘Clase XYZ’, definiendo un interfaz general (‘AbstractAlgorithm’) para el grupo de algoritmos que queremos codificar, y encapsular cada uno de estos en una clase (‘Algorithm 1’, ‘Algortithm 2’, etc), haciéndolos intercambiables.

Por qué son útiles los patrones

Hemos dicho que los patrones de diseño han contribuido a dar flexibilidad y extensibilidad a nuestros diseños. Pero en adición, han demostrado ser una forma muy útil (exitosa) de reutilizar diseño, ya que ellos no sólo nombran, abstraen e identifican aspectos claves de estructuras comunes de diseño, sino que generalmente son descritos en una forma específica documental, haciendo su comprensión y aplicación fácil para el conjunto de desarrolladores.

Podemos decir que los beneficios que un patrón produce pueden ser medidos en varios sentidos:

  • Contribuyen a reutilizar diseño, identificando aspectos claves de la estructura de un diseño que puede ser aplicado en una gran cantidad de situaciones. La importancia de la reutilización del diseño no es despreciable, ya que ésta nos provee de numerosas ventajas: reduce los esfuerzos de desarrollo y mantenimiento, mejora la seguridad, eficiencia y consistencia de nuestros diseños, y nos proporciona un considerable ahorro en la inversión.
  • Mejoran (aumentan, elevan) la flexibilidad, modularidad y extensibilidad, factores internos e íntimamente relacionados con la calidad percibida por el usuario.
  • Incrementan nuestro vocabulario de diseño, ayudándonos a diseñar desde un mayor nivel de abstracción.

Qué son los Core J2EE Patterns

El 8 de Marzo del 2001, el Centro Java de Sun publicó (como beta) un catálogo de patrones .

Los patrones J2EE describen típicos problemas encontrado por desarrolladores de aplicaciones empresariales y proveen soluciones para estos problemas. En esencia, estos patrones contienen las mejores soluciones para ayudar a los desarrolladores a diseñar y construir aplicaciones para la plataforma J2EE.

Aunque estos patrones son representados desde un nivel lógico de abstracción, todos contienen en sus descripciones originales en la web de Sun, varias estrategisa que ofrecen una gran cantidad de detalles para su implementación.

En los primeros 60 días en los que estos patrones estuvieron a disposición de los desarrolladores (en forma de documentos Html), fueron accedidos más de 120.000 veces, lo que evidencia el éxito de estas soluciones dentro de la comunidad Java.

Diseño de aplicaciones web con los Core J2EE Patterns

l diseño de aplicaciones Web basadas en los Patrones J2EE se organiza alrededor de la utilización de varios elementos: un controlador frontal, dispatchers (despachadores), vistas compuestas (composite views), vistas (JSPs) y los helpers (ayudantes) de las vistas (JavaBeans).

Todos estos elementos se pueden organizar en el siguiente diseño, que ejemplifica un diseño basado en los patrones J2EE. Básicamente se puede considerar en sí un patrón, en concreto el ‘Service-To-Worker Extendido’:

Service-To-Worker Extendido

Veamos una descripción básica de estos elementos:

  • Front Controller (controlador frontal). Este elemento provee un controlador centralizado para gestionar las peticiones webs a la aplicación. Un controlador frontal recibe todas las peticiones entrantes de los clientes, remitiendo a su vez cada petición al gestor de peticiones (Dispatcher) adecuado, que se encargará de gestionar la construcción de una respuesta adecuada al cliente. Son los puntos ideales para implementar servicios de seguridad, tratamiento de errores, y la gestión del control para la generación de contenidos.
  • Dispatcher : Tendremos toda una colección de estos elementos. En cada uno codificaremos la construcción de la respuesta al usuario. Básicamente lo que hacen es componer vistas y configurar estas para que muestren la información adecuada como respuesta a la petición del usuario.
  • Composite View (Vista Compuesta). Este patrón hace que la representación de vistas sea más manejable ya que gestiona los diferentes elementos de una página por medio de una plantilla. Frecuentemente, las páginas webs contienen una combinación de contenido dinámico y elementos estáticos, tales como cabeceras, pies, logos, imágenes de fondo, etc. La parte dinámica es particular para cada página, pero los elementos estáticos suelen ser los mismos para todas las páginas. La plantilla de la vista compuesta captura estas características comunes. La integración debe ser dinámica, siendo el Composite View básicamente un layout (diseño, esquema) que componga dicha página.
  • Vistas. Páginas JSPs. Se encargan de generar contenido visual específico que responda a las necesidades del usuario. Las páginas JSPs por lo general estarán parametrizadas de tal forma que muestren diferente información según los parámetros que le mandemos. Por lo general una vista (JSP) produce un trozo de la página web que recibe el usuario.
  • View Helper (Ayudante o Auxiliar de Vista). Un ‘View Helper’ encapsula los trozos de lógica (código java) correspondientes a la presentación y al acceso a datos y componentes que necesita una vista, haciendo que la vista permanezce de esta forma mucho más simple, reutilizable y mantenible. La lógica de presentación se encarga de formatear datos para que sean visualizados en una página, mientras que el acceso a datos o componentes implica la obtención de datos. Los View Helpers suelen ser JavaBeans.

ServiceToWorker. Es un patrón que representa la unión como solución de los patrones: controller, dispatcher, view y helper (considerando que existen helpers para el controller, el dispatcher y las views). Básicamente el diseño presentado se puede considerar una versión extendida de este patrón.

Veamos con un diagrama de secuencia cómo interactúan todos estos elementos:

Service-To-Worker Extendido

El frontcontroller recibiría una petición (por ejemplo: www.MiAplicacion.com/controller?op=VerRegistro&id=58), este comprobará que para ejecutar dicha operación (mostrar el registro cuyo id=58) el usuario tiene permiso, y delegará la realización de dicha acción al objeto Dispatcher. Este configurará la vista compuesta de tal forma éste monte una página con contenidos estáticos y que además llamará (o incluirá) a la JSP que se encarga de visualizar registros. La página JSP tendrá todo el código HTML que se utiliza en la visualización y obtendrá los datos a visualizar llamando a un Bean que se encargará de comunicarse con la base de datos u otros componentes que hagan esta función.

Toda esta estructura es básicamente una implementación de la conocida arquitectura Model-View-Controller (MVC). Básicamente en el modelo MVC, lo que hace el controller es filtrar peticiones, extraer datos, mapear peticiones a comandos, invocar comandos para gestionar estas peticiones. La parte de Vista determina páginas a visualizar, y formatea la página para su visualización. El cliente interacciona con estas vistas, y en curso de dicha interacción hace peticiones http.

Patrones J2EE útiles para el diseño web

En este apartado vamos a hacer un desarrollo pormenorizado de los patrones J2EE fundamentales para el desarrollo de aplicaciones web que son los que hemos usado en el diseño presentado anteriormente:

  • Patrón Front Controller (Controlador Frontal)

    La gestión de las peticiones http (como por ejemplo www.MiAplicacion.com/controller?op=VerRegistro&id=58) a nuestra aplicación web puede ser centralizada o distribuida. Como ya hemos visto anteriormente, tener distribuida la gestión de peticiones en nuestras páginas jsp lleva a aplicaciones de baja calidad, en las que generamos mucho código similar y distribuido por todas nuestras páginas (vistas).

    Solución: Usar un controlador como punto inicial para la gestión de las peticiones. El controlador gestiona estas peticiones, y realiza algunas funciones como: comprobación de restricciones de seguridad, manejo de errores, mapear y delegación de las peticiones a otros componentes de la aplicación que se encargarán de generar la vista adecuada para el usuario.

    Centralizando los puntos de decisión y control, el controlador ayuda a reducir la cantidad de código java que se encuentra embebido en nuestras páginas jsp (a este código se le suele llamar Scriptlet). Lo que hacemos es centralizar control en el controlador y reducir lógica de negocio en las vistas (un modelo ejemplar de aplicación del metapatrón 'encapsulator/centralizer').

    Patrón Front Controller

    La implementación típica de un controlador suele ser un Servlet.

    import javax.servlet.*;
    import javax.servlet.http.*;
     
    public class Controller extends HttpServlet {
     
        public void init(ServletConfig config) throws ServletException {
           super.init(config);
        }
     
        public void destroy() {
        }
     
        protected void doGet(HttpServletRequest request, 
            HttpServletResponse response)
            throws ServletException, java.io.IOException {
     
            processRequest(request, response);
        }
     
        protected void doPost(HttpServletRequest request, 
            HttpServletResponse response)
            throws ServletException, java.io.IOException {
     
            processRequest(request, response);
        }
         
        protected void processRequest(HttpServletRequest request, 
            HttpServletResponse response)
            throws ServletException, java.io.IOException {
     
            // obtiene el parámetro op (operación) de la solicitud 
            String action = request.getParameter("op") ;
            // el directorio al que enviará la petición "despachada"
            String dispatch ="/servlet/" ;
     
            // Dependiendo de la acción pasada "despacha"
            // a un servlet distinto
            if(action == null)
                dispatch += "Inicio" ;
            else if(action.equals("VerRegistro"))
                  dispatch += "VisualizarRegistro" ;
            else if(action.equals("ListRegistros"))
                dispatch += "ListarRegistros" ;
     
            // crea el dispatcher al que reenviarle la solicitud
            RequestDispatcher dispatcher = 
                 request.getRequestDispatcher(dispatch) ;
     
            // reenvia la petición
            dispatcher.forward(request, response) ;
        }
     
        public String getServletInfo() {
            return "";
        }
    }

    Consecuencias: centralizamos control, mejoramos la manejabilidad de la seguridad, y aumentamos la reusabilidad del código.

  • Dispacher View

    Problema: No hay un componentes centralizado para la gestión del control de acceso, la recuperación de contenidos o la gestión de las vistas, y existe código duplicado a través de múltiples vistas. Adicionalmente, la lógica de negocio y la lógica de formateo de la presentación están mezcladas en estas vistas, haciendo al sistema menos flexible, menos reusable, y generalmente menos resistente al cambio, reduciendo también su modularidad, y proveyendonos de una pobre separación de roles entre la producción web y los equipos de desarrollo de software.

    Solución: Combinar un controlador y un despachador con vistas para manejar las peticiones de los usuarios y preparar una presentación dinámica como respuesta. Un dispatcher es responsable de la gestión de vistas y de la navegación y puede ser encapsulado tanto dentro de un controlador, una vista, o como un componente separado.

    Patrón Dispacher View

    La implementación del elemento Dispatcher suele ser un Servlet que hace comprobaciones sobre los parámetros que le pasa el usuario que le pide su ejecución, así como llamar a la página JSP adecuada pasándole a su vez los atributos necesarios para que esta se ejecute y visualize el resultado adecuado. En un entorno de Vistas Compuestas debería también configurar esta última.

    Consecuencias: centraliza control y mejora la reusabilidad y la mantenibilidad, mejora el particionamiento de la aplicación y mejora la separación de roles.

  • Vistas (JSPs)

    Las vistas o JSPs son la forma de separar la visualización de la información de la lógica de negocio necesaria para generarla, de tal forma que la plataforma en la que se gestionen esos datos, y el servidor web sean independientes.

    Las JSPs son ficheros que generalmente se componen de código HTML estático, etiquetas específicas JSPs, y opcionalmente, trozos de código java llamados 'Scriptlets'.

    Típicamente, las páginas JSPs están sujetas a una fase de traducción a a una fase de procesamiento de peticiones. La fase de traducción se lleva a cavo una sola vez, a no ser que la JSP cambie, y por lo tanto se tenga que repetir este proceso. Asumiendo que no hay errores sintácticos en la página, el resultado de este proceso es un fichero de clase, que implementa dentro de un servlet la funcionalidad que hayamos puesto en la JSP.

  • Composite View

    Contexto: Las aplicaciones web sofisticadas presentan contenido de numerosas fuentes de datos, usando múltiples subvistas, que conforman una única página a visualizar.

    Solución: Usar vistas compuestas que están formadas por múltiples subvistas atómicas, Cada componente de la plantilla puede ser incluido dinámicamente en el total, y el esquema de la página puede ser gestionado de forma independiente al contenido.

    Este escenario ocurre, por ejemplo, en portales web que incluyen numerosas e independientes subvistas.

    Un requisito típico de las aplicaciones web es el que su visualización tenga un estructura similar a lo largo de todo el web. Una plantilla es un componente de presentación que compone vistas separadas en una única página con un diseño específico.

    Plantilla

    El diseño de clases de este patrón es el siguiente:

    Patrón Composite View

    El mecanismo para implementar este patrón se basa en el uso de la etiquete <jsp:include> que puede incluir tanto contenido estático como contenido dinámico en la página cuando esta está siendo servida.

    Un ejemplo de página JSP que implementa una Vista Compuesta:

    <%@ page contentType="text/html;charset=windows-1252"%>
    <html>
    <head>
    <title>Vista Compuesta</title>
    </head>
    <body>
    <%
      String sMainPanel;
      String sHeader, sFooter, sLeft, sRight, sArgs;
      sMainPanel = (String)request.getAttribute("mainPanel");
      sHeader = (String)request.getAttribute("header");
      sFooter = (String)request.getAttribute("footer");
      sLeft = (String)request.getAttribute("left");
      sRight = (String)request.getAttribute("right");
      sArgs = (String)request.getAttribute("args");
    %>
    <jsp:include page="<%= sHeader%>" flush="true"/>
    <table width="100%">
      <tr align="left" valign="middle">
        <td width="20%">
    	<jsp:include page="<%= sLeft%>" flush="true"/>
    	</td>
    	<td width="70%" align="center">
    	<jsp:include page="<%= sMainPanel + "?" + sArgs %>" flush="true"/>
    	</td>
    	<td width="70%" align="right">
    	<jsp:include page="<%= sRight%>" flush="true"/>
    	</td>
      </tr>
    </table>
    <jsp:include page="<%= sFooter%>" flush="true"/>
    </body>
    </html>

    Consecuencias: Mejora de la modularidad y la reutilización, mejora la flexibilidad, el mantenimiento, y la manejabilidad, tiene un impacto negativo (aunque muy pequeño) en el rendimiento.

  • View Helper

    Contexto: El sistema crea presentaciones del contenido, lo que requiere procesamiento dinámico de datos de negocio.

    Problema: Es frecuente hacer cambios en la capa de presentación de una aplicación, y esto es difícil de hacer cuando la lógica de acceso a datos de negocio, la lógica de formateo de la presentación está mezclada en esta capa.

    Solución: Un vista contiene el código de formateo, delegando sus responsabilidades de procesamiento a sus clases helper, implementadas generalmente como JavaBeans (o custom tags). Los helpers también guardan los modelos de datos intermedios de la vista y sirven como adaptadores de los datos de negocio.

    Patrón View Helper

    JAVABEANS:

    Los JavaBeans no son más que objetos java que siguen cierto patrón bien definido: el bean encapsula sus propiedades declarándolas privadas y provee métodos de acceso públicos (getter/setter) para leer y modificar los valores de estas propiedades.

    Antes de poder acceder a un Bean dentro de una página JSP, es necesario identificarlo y crear una referencia al mismo, lo cual se hace usando la etiqueta <jsp:useBean ...>.

    Para obtener el valor de una propiedad de un bean se utiliza la etiqueta

    <jsp:getProperty name="user" property="name" />

    y para modificarla

    <jsp:setProperty name="user" property="name" value="<%=expression %>" />

    Aquí tenemos un ejemplo de página JSP que hace uso de un JavaBean para mostrar el contenido de un registro . Le pasamos su 'ID', y obtenemos las propiedades de dicho registro: Nombre, Código Interno y Descripción. La implementación del JavaBean es muy sencilla al ser simplemente una clase que, generalmente, hace uso de JDBC para acceder a bases de datos, o que delega la responsabilidad de leer estos datos llamando a otro componente.

    <%@ page  language="java"
              contentType="text/html;charset=ISO-8859-1"
    		  import="ejemplos.VerRegistroBean"%>
    <jsp:useBean id="VerRegistroBean" scope="session" 
                 class="ejemplos.VerRegistroBean" />
    <jsp:setProperty name="Id" property="<%= request.getParameter("id");%>" />
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Visualización del Registro</title>
    </head>
    <body>
    <table width="100%">
      <tr align="left" valign="middle">
        <td>Nombre:</td>
    	<td>
    	<jsp:getProperty name="VerRegistroBean" property="sNombre" />
    	</td>
      </tr>
      <tr>
        <td>Código Interno:</td>
    	<td>
    	<jsp:getProperty name="VerRegistroBean" property="iCodInterno" />
        </td>
      </tr>
      <tr>
        <td>Descripción:</td>
        <td>
        <jsp:getProperty name="VerRegistroBean" property="sDesc" />
        </td>
      </tr>
    </table>
    </body>
    </html>

    Consecuencias: mejora el particionamiento de la aplicación, su reutilización, y su mantenibilidad, así como la separación de Roles.

  • Service to Worker

    Contexto: El sistema contorla el flujo de ejecución y acceso a los datos de negocio, desde el que se crea la presentación de contenidos.

    Problema: El problema es una combinación de los problemas resueltos por los patrones controlador frontal y View helper. No existe un componente que centralice la gestión del control de acceso, la recuperación de contenido, la administración de las vistas, y existe código de control duplicado y esparcido en múltiples vistas. Adicionalmente la lógica de presentación, y la lógica de formateo de presentación están mezcladas en estas vistas, haciendo el sistema menos flexible, menos reusable y generalemente menos resistente al cambio.

    Solución: Combinar un controlador, un dispatcher con vistas, y helpers para gestionar las peticiones de los clientes, y preparar una presentación dinámica como respuesta. Los Controllers delegan la recuperación de contenido a los helpers. Un dispatcher es el responsable de la gestión de vistas y la navegación.

COMPARTE ESTE ARTÍCULO

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

SIGUIENTE ARTÍCULO