-
Patrones de diseño
Índice
1 Charla 1: Introducción a los patrones de diseño. Algunos
patrones básicos................3
1.1 Por qué
patrones.......................................................................................................3
1.2 Patrones
J2EE..........................................................................................................
4
1.3 Singleton
(GoF).......................................................................................................
6
1.4 Facade
(GoF)...........................................................................................................
8
1.5 Factory
(GoF).........................................................................................................10
2 Ejercicio de la charla 1: Introducción a los patrones de
diseño................................. 15
2.1 Refactorización basada en
patrones.......................................................................
15
3 Charla 2: Patrones para las capas de presentación y
negocio.....................................16
3.1 Arquitecturas para la capa de presentación en aplicaciones
web.......................... 16
3.2 Modelo-Vista-Controlador
(MVC)........................................................................18
3.3 Algunos patrones para la capa de
negocio.............................................................24
4 Ejercicio de la charla 2: Patrones para aplicaciones
web........................................... 26
4.1 La aplicación de
bookmarks..................................................................................
26
4.2 Implementación de un caso de uso con
MVC....................................................... 27
4.3 Caso de uso "crear nuevo bookmark"
(*)..............................................................
27
5 Charla 3: Patrones para aplicaciones
enterprise.........................................................
28
5.1 Patrones para aplicaciones distribuidas vs. aplicaciones
"locales"........................28
5.2 Fuentes bibliográficas para patrones
enterprise.....................................................28
5.3 Minimizando las llamadas remotas: Session
Facade............................................. 29
5.4 Transfiriendo datos a los clientes: Transfer
Object............................................... 31
5.5 Localizando servicios: Service
Locator.................................................................
32
5.6 Haciendo transparentes los EJBs de negocio:Business
Delegate.......................... 34
6 Ejercicio de la charla 3: Patrones para aplicaciones
enterprise..................................35
6.1 Estructura de la
aplicación.....................................................................................
36
6.2 Patrón
fachada........................................................................................................36
Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
6.3 Patrón Business Delegate
(*).................................................................................
37
Patrones de diseño
2Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
1. Charla 1: Introducción a los patrones de diseño. Algunos
patronesbásicos
Aunque cada aplicación J2EE tiene peculiaridades que la hacen
única, en el proceso dedesarrollo de casi todas las aplicaciones es
necesario solucionar una y otra vez los mismosproblemas:
autentificación del cliente, persistencia de datos, separación
entrepresentación, lógica y control,... En lugar de reinventar
continuamente la rueda, es muchomás productivo aplicar estrategias
que ya hayan funcionado con anterioridad. Esta idea esla que lleva
a la definición de los patrones software.
1.1. Por qué patrones
En ingeniería del software, un patrón (pattern) es una solución
ya probada y aplicable aun problema que se presenta una y otra vez
en el desarrollo de distintas aplicaciones y endistintos contextos.
Es importante destacar que un patrón no es en general una solución
enforma de código directamente "listo para usar", sino más bien una
descripción de cómoresolver el problema y de ante qué
circunstancias es aplicable.
Los patrones software fueron popularizados en el libro Design
Patterns: Elements ofReusable Object-Oriented Software, que trata
de patrones genéricos, aplicables a unaamplia gama de contextos y a
cualquier lenguaje orientado a objetos. Este libropopularizó el
"movimiento" de patrones y se ha convertido en un clásico,
ampliamentereferenciado y que muchos han tomado como base para
añadir patrones nuevos. Losautores de Design Patterns... son Erich
Gamma, Richard Helm, Ralph Johnson y JohnVissides, aunque de manera
jocosa y popular son más conocidos como el Gang of Four.De hecho,
en muchas publicaciones serias los patrones que aparecen en Design
Patternsse referencian con las siglas GoF.
Ante todo este floreciente movimiento en torno a los patrones
cabe preguntarse sirealmente aportan beneficios. Se suele
argumentar que los patrones ofrecen tres ventajasfundamentales:
• Están ya probados: son soluciones que han sido utilizadas con
anterioridad demanera repetida y se ha comprobado que
funcionan.
• Son reutilizables: corresponden con problemas que no son
específicos de un casoconcreto, sino que se presentan una y otra
vez en distintas aplicaciones.
• Son expresivos: cuando un equipo de desarrolladores tiene un
vocabulario común depatrones, se puede comunicar de manera fluida y
precisa las ideas fundamentalessobre el diseño de una
aplicación.
Por supuesto, los patrones no pueden ser la solución a todos los
problemas de diseño ydesarrollo de aplicaciones J2EE. Como
cualquier herramienta o metodología sonsusceptibles de malos usos,
y de abusos (uso "compulsivo" simplemente "porque sonbuenos"). La
experiencia y el sentido común dictarán cuándo son apropiados y
cómo
Patrones de diseño
3Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
utilizarlos.
1.2. Patrones J2EE
Los patrones J2EE están orientados específicamente a los
problemas comunes a todaslas aplicaciones J2EE. Algunos están
basados en los patrones originales mientras queotros son más
específicos del tipo de problemas que surgen específicamente en
J2EE,bien sea por los tipos de aplicaciones que se suelen
desarrollar con la plataforma o por lascaracterísticas (o
deficiencias, incluso) de la tecnología. Los primeros fueron
publicadosen el libro Core J2EE Patterns, convertido también en un
clásico dentro del "mundillo"J2EE. En la actualidad son muchos los
libros y los sitios web dedicados íntegramente apatrones para
aplicaciones J2EE o con algún apartado sobre ellos.
Una versión resumida del catálogo de Core J2EE Patterns está
disponible en su sitio web.Siguiendo la idea de dividir la
arquitectura de una aplicación en varias capas, los patronesse
clasifican atendiendo a la capa a la que pertenecen. En la figura 5
aparece el esquemageneral en la que se muestra la situación de cada
uno de los 21 patrones en el modelo decapas y las relaciones que
existen entre ellos. Iremos viendo con detalle algunos de
estospatrones y sus interrelaciones en las distintas charlas del
módulo.
Patrones de diseño
4Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Catálogo de patrones de Core J2EE Patterns
En los patrones que vamos a ver en el módulo mezclaremos por
igual patrones J2EE conpatrones genéricos. El resultado es una
lista de patrones bastante ad-hoc, en cuanto alorden de explicación
que seguiremos y en cuanto a los patrones que trataremos frente
alos que dejaremos de lado. Hay que tener en cuenta que el objetivo
no es dar una listaextensiva de los mismos. Si a los 21 patrones
J2EE se les suman los 23 fundamentales delGoF surge un número
demasiado elevado para las horas disponibles. Por ello se ha
optadopor describir solo algunos. El criterio de inclusión no ha
sido su importancia per se, sinoel hecho de que sea un patrón de
uso habitual en aplicaciones J2EE. Para una lista depatrones más
"estándar" se puede consultar cualquiera de los recursos
especificados en labibliografía.
Patrones de diseño
5Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Junto al nombre de cada patrón especificaremos si se trata de un
patrón J2EE o procededel GoF.
1.3. Singleton (GoF)
1.3.1. Problema a resolver
Hay muchos casos en los que solo necesitamos una instancia de
una determinada clase.Ejemplos típicos son clases que representan
las preferencias del usuario o laconfiguración del sistema, o
clases que sirven de interfaz con dispositivos físicos.
En estos casos hay que asegurarse de poder obtener una
referencia a dicha instancia desdecualquier punto del código, y que
solo haya una instancia creada, para evitar posiblesproblemas o
inconsistencias. Una posible solución es definir variables globales
(o sea,static), pero esto tiene dos problemas:
• Por descuido, se podría instanciar una misma variable en dos
sitios distintos, lo que"machacaría" el valor anterior.
• El orden y momento de inicialización de las variables depende
del compilador, lo cualpuede ser delicado si unas dependen de otras
y están en lugares distintos.
1.3.2. Discusión y beneficios
El patrón singleton nos permite asegurar que de una clase habrá
solo una instancia, yproporciona un punto de acceso a ella global a
todo el código. El diagrama de clases esmuy sencillo, ya que se
compone de una única clase:
Patrón singleton
El método getInstance() nos sirve para obtener la referencia a
la única instancia de laclase. Además dicha instancia está
almacenada dentro de la propia clase como unavariable de tipo
static (esto último puede resultar curioso, pero no deja de ser un
"trucoingenioso" perfectamente legal para el compilador). Por
supuesto el singleton tendrá otrosmétodos, los servicios que
proporcione la clase.
La implementación de un singleton podría hacerse de varias
formas, pero casi siempre seutiliza el mismo tipo de código. Vamos
a resumir las ideas que nos llevarán a laimplementación final:
• Si el constructor de una clase es público, podrá llamarse
desde cualquier otra. Portanto, si queremos asegurar un control de
la creación de instancias, no podemos tenerun constructor público,
debemos hacerlo privado:
Patrones de diseño
6Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
public class MiSingleton {private MiSingleton() {
//aqui va el codigo del constructor}
}
• Evidentemente, aunque el código anterior sea una definición
legal que compilaráperfectamente, no parece tener mucho sentido. Si
el constructor es privado, solo sepodrá llamar desde un objeto de
la clase MiSingleton. Pero ¡no podemos instanciarobjetos de esa
clase, porque el constructor es privado!. La solución al aparente
dilemaconsiste en utilizar un método estático para llamar al
constructor:
public class MiSingleton {private MiSingleton() {
//aqui va el codigo del constructor}public static MiSingleton
getInstance() {
return new MiSingleton();}
}
//código aparte...MiSingleton ms =
MiSingleton.getInstance();
• Con esto ya conseguimos controlar el acceso al constructor y
poder llamarlo desdefuera de la clase. Lo único que nos falta es
asegurarnos de que en todo momentoexiste una única instancia de
MiSingleton. El "truco" consise en almacenar dichainstancia dentro
de la propia clase MiSingleton (como una variable static) y
enintroducir código en getInstance que chequee si dicha instancia
está creada o no,para devolverla o en caso contrario devolver un
nuevo MiSingleton.
public class MiSingleton {//la unica instancia que existe de
esta claseprivate static MiSingleton unico = null;
private MiSingleton() {//aqui va el codigo del constructor
}public static MiSingleton getInstance() {
//instanciar el singleton si no existeif (unico == null) {
unico = new MiSingleton();}//devolver el singletonreturn
unico;
}}//código aparte...MiSingleton ms =
MiSingleton.getInstance();
Como puede verse, este código es una especie de "idea feliz" que
consigue de formaelegante el objetivo que nos proponíamos. Aunque
quizá esto podría conseguirse de otras
Patrones de diseño
7Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
formas, esta es ampliamente utilizada y conocida, por lo que
merece la pena usarla enlugar de intentar formas propias de hacerlo
(después de todo, esta "reutilización de ideas"es consustancial a
la idea misma de patrones software).
Aviso:El código visto no asegura que solo exista una instancia
si se permiten varios threads (piénseseque pasaría si los dos
threads comprueban "a la vez" que no existe una instancia del
objeto y lacrean ¡por dos veces!). Consultad la bibliografía
adicional para ver formas de solucionar esteproblema (basadas en
general en el uso de synchronized).
1.3.3. Relación con otros patrones
En aplicaciones J2EE, son múltiples los casos en los que solo se
necesita una instanciade un objeto para toda la aplicación (aunque
a este objeto lo puedan llamar varios threadssimultáneamente).
Piénsese por ejemplo en un objeto encargado de calcular costes
deenvío de pedidos. Los costes según métodos de envío, plazos de
recepción, peso delpaquete, etc. son información global para toda
la aplicación, de modo que se puedeimplementar como un
singleton.
Un uso típico de este patrón en aplicaciones enterprise es para
implementar un encargadoglobal de localizar recursos JNDI como por
ejemplo conexiones JDBC, EJBs, etc. Esto eslo que se conoce como
Service Locator.
1.4. Facade (GoF)
1.4.1. Problema a resolver
Supongamos que tenemos un sistema complejo, que agrupa multitud
de clases, y pararealizar una tarea tenemos que llamar a varios
métodos de estas clases en una secuenciaprecisa. Por ejemplo,
supongamos un sistema domótico en el que tenemos el
siguientediagrama de clases
Patrones de diseño
8Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Sistema domótico inicial
Imaginémonos las operaciones que hay que realizar al entrar en
casa: hay que desactivarla alarma, poner el aire acondicionado a
nuestra temperatura preferida, y encender lasluces. Esto se podría
hacer con un código similar al siguiente:
...//de alguna forma, ya hemos obtenido una referencia a la
alarma,configuración, aire y luces//y tenemos también el nombre de
usuario y el password...//desactivar la
alarmaalarma.desactivar(password);//obtener preferencias de
usuarioPreferencias prefs =
configuracion.getPreferencias(usuario);//poner el aire
acondicionado a la temperatura deseadaFloat temp = (Float)
prefs.getPreferencia("temperatura");aire.setTemp(temp.floatValue());//poner
las luces en "auto" si es la preferencia del usuarioString luces =
(String) prefs.getPreferencia("luces");if
(luces.equals("auto"))
luces.setAuto(true);...
Como se ve, una operación tan sencilla en apariencia implica
manejar un número elevadode objetos y métodos. salirDeCasa() sería
igual de tedioso: apagar las luces, el aire,activar la alarma....
Un cliente que quiera invocar una de estas operaciones no
deberíanecesitar tanto código.
1.4.2. Discusión y beneficios
Parece inmediata la idea de crear una clase a la que
trasladaremos todo este código y a lacual puedan acceder los
clientes que lo necesiten. Esto es ni más ni menos que un
facade.
Patrones de diseño
9Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Patrón facade
Un Facade (fachada) consiste en implementar un interfaz
simplificado para unsistema complejo. La idea es implementar una
clase con un interfaz sencillo y queencapsule los detalles de la
interacción entre todas las clases del sistema. Es importantenotar
que se sigue permitiendo el acceso directo a las clases del sistema
a los clientes quenecesiten "acceso a bajo nivel" pero se
simplifica la interacción para los que no necesitenmás que
operaciones comunes.
En aplicaciones J2EE, las fachadas se suelen utilizar para
proporcionar un "frontal deservicios" de la capa de negocio. De
este modo, la interacción de los clientes (web, swing,etc...) con
esta capa se simplifica considerablemente. Como veremos en
charlasposteriores, en aplicaciones distribuidas las fachadas
también mejoran la eficiencia delsistema, ya que las operaciones
"de fachada para adentro" serán todas locales y la únicallamada
remota será la del cliente a la fachada.
1.5. Factory (GoF)
1.5.1. Problema a resolver
El patrón factory pretende proporcionar una buena manera de
instanciar objetos cuando laclase a la que pertenece el objeto
instanciado puede cambiar, bien por modificaciones enel diseño o
bien porque en tiempo de compilación no se conoce la clase
exacta.
Por poner un ejemplo concreto, supongamos que tenemos un sistema
de mensajeríainstantánea. Los mensajes se pueden enviar a través de
varios canales (TCP/IP, SMS,buzón de mensajes,...) y hemos
implementado una serie de clases que nos permiten hacerel envío por
ellos: EnvioTCP, EnvioSMS, EnvioBuzon,... todas estas clases
implementan lamisma interfaz ICanal, y el usuario elige el medio a
través del GUI del programa. Elcódigo que envía el mensaje podría
ser similar al siguiente:
Patrones de diseño
10Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Mensaje mensaje;ICanal canal;...mensaje =
GUI.getMensaje();nombreCanal = GUI.getOpcionEnvio();if
(nombreCanal.equals("TCP"))
canal = new EnvioTCP();else if (nombreCanal.equals("SMS"))
canal = new EnvioSMS();else if (nombreCanal.equals("buzon"))
canal = new EnvioBuzon();canal.enviar(mensaje)
El código anterior es tedioso de modificar si se cambian los
posibles canales de envío oaparecen canales nuevos (bluetooth,
...).
Nota:Un principio básico de un buen diseño es que el código
debería estar abierto a la extensiónpero cerrado a la modificación.
Es decir, que el diseño debería ser tal que nos permitieraextender
la funcionalidad donde fuera necesario, pero al mismo tiempo
consiguiera que estaextensión no suponga cambios en el código ya
existente. Cualquier modificación de código enfuncionamiento podría
suponer la introducción de bugs en el sistema.
Es evidente que el código anterior no cumple esta condición, ya
que es necesario insertarlíneas de código y recompilar la clase
para extender la funcionalidad.
1.5.2. Simple factory
Recordemos de nuevo ese principio básico del diseño que dice que
hay que separar lo quevaría de lo que permanece igual. En nuestro
caso hemos visto que al tomar la precauciónde usar una interfaz
común para todas las clases lo que puede cambiar es la
instanciaciónde la clase concreta que necesitamos. Por tanto, vamos
a encapsularla y separarla delresto.
Si tuviéramos una clase auxiliar que nos proporcionara
instancias concretas de la clase ointerfaz deseados (en nuestro
caso ICanal) podríamos hacer algo como:
Mensaje mensaje;ICanal canal;...mensaje =
GUI.getMensaje();nombreCanal = GUI.getOpcionEnvio();canal =
FactoriaCanales.crearCanal(nombreCanal);canal.enviar(mensaje)
Que es un código mucho más limpio que la versión anterior. Ahora
trasladamos losdetalles de la instanciación a FactoriaCanales, que
se ha convertido en una especie defactoría o fábrica de
objetos:
Patrones de diseño
11Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
public class FactoriaCanales {public static ICanal
crearCanal(String nombre) {ICanal canal;
if (nombre.equals("TCP"))canal = new CanalTCP();
else if (nombre.equals("SMS"))canal = new CanalSMS();
else if (nombre.equals("buzon"))canal = new CanalBuzon();
return canal;}}
A primera vista parece que simplemente hemos trasladado el
problema a la claseFactoriaCanales. Es parcialmente cierto, en el
sentido en que todavía es necesariomodificar esta clase si se
modifican o crean canales nuevos, pero no será necesariomodificar
ni recompilar ninguna de las clases que llamen al
métodoFactoriaCanales.crearCanal (que podrían ser muchas). Hemos
acotado los cambiosnecesarios a una clase únicamente.
Usando la terminología de patrones, la clase anterior es una
"factoría simple" (simplefactory). No es exactamente el patrón
factory sino una versión simplificada de este.Podéis compararar el
diagrama UML del simple factory con el del factory del
apartadosiguiente para ver las diferencias.
Simple factory
En el diagrama anterior, Cliente es cualquier clase que requiera
los "servicios" delSimple Factory para crear objetos.
Aviso:En algunas fuentes de referencia se da como patrón factory
esto que no es sino una versiónsimplificada. Esto no supone un
problema siempre que se tenga clarpo de qué se está hablando yla
diferencia entre ambas "versiones".
Patrones de diseño
12Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
1.5.3. Factory method
Supongamos que el problema anterior se complica: ahora tenemos
que distinguir entreusuarios lite, que no pagan una cuota y por
tanto tienen algunas restricciones (númeromáximo de SMS, tiempo
máximo de conexión, tamaño más limitado del buzón,..) yusuarios
pro, que tienen menos restricciones en la mensajería. Necesitamos
por tantocrear un canal lite y pro de cada tipo (SMS, TCP,...).
Para organizar el código de maneraflexible podemos usar el patrón
factory method, a veces llamado simplemente factory.
Este patrón es ligeramente más complicado que la versión
anterior. Tenemos una factoría"genérica" que es una clase abstracta
y sirve para "fabricar" un producto, tambiéngenérico (un interfaz o
bien una clase abstracta). Siguiendo con nuestro ejemplo,tendríamos
una clase abstracta FactoriaCanales cuyo método crearCanal
devolveríaun nuevo ICanal. Las factorías "concretas" (clases que
heredan de la clase de la factoría"genérica") sirven para
"fabricar" productos concretos. En nuestro caso tendríamos
dosclases que heredarían de FactoriaCanales: FactoriaCanalesLite
yFactoriaCanalesPro, cuyo método crearCanal(tipo) devolvería
objetos CanalTCP,CanalSMS, de tipo lite y pro respectivamente. La
siguiente figura muestra el diagrama declases para el ejemplo.
Factory method para objetos ICanal
Esta es una forma del patrón que, aunque más compleja que el
simple factory es másflexible y permite extender el sistema de una
manera más elegante. Por ejemplo, laaparición de un nuevo tipo de
usuarios digamos silver, con privilegios intermedios,conllevaría la
creación de nuevas clases, como CanalSMSSilver y
CanalTCPSilver,cuyas instancias las crearía una factoría
FactoriaCanalesSilver. La ventaja de esteenfoque es que no requiere
modificar prácticamente nada del código ya existente. Lasclases que
envían y reciben mensajes simplemente trabajan con objetos ICanal y
lasclases que necesiten obtener nuevas instancias de canales
simplemente necesitan unareferencia al FactoriaCanales del tipo
adecuado. Cambiando una simple línea de código(o un fichero de
configuración) podemos convertir a un usuario lite en pro sin más
quecambiar su FactoriaCanalesLite por una FactoriaCanalesPro.
La siguiente figura muestra el diagrama de clases genérico del
patrón.
Patrones de diseño
13Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Factory method: diagrama genérico
Nótese que no siempre es necesario que un factory concreto cree
más de un productodistinto. En nuestro ejemplo, cada subclase de
FactoriaCanales creaba distintos tipos decanal, pero no siempre es
así. En ese caso el método que actúa de factoría de objetos
notendrá parámetros.
Patrones de diseño
14Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
2. Ejercicio de la charla 1: Introducción a los patrones de
diseño
2.1. Refactorización basada en patrones
En esta sesión vamos a refactorizar el código de una aplicación
basándonos en lospatrones de diseño vistos en la charla.
La aplicación consiste en un lector de feeds RSS en el que
podemos suscribirnos a unaserie de feeds y leer su contenido.El
código de la aplicación se incluye en las plantillas dela sesión.
Para simplificar el código del ejercicio, la aplicación carece de
interfaz gráfico.
Nuestro objetivo es refactorizar la parte de la aplicación que
lee y guarda laconfiguración de cada uno de los feeds, teniendo en
cuenta que:
• La clase RSSReader es la clase principal, encargada de bajarse
los feeds de Internet ymostrarlos adecuadamente formateados.
• La configuración de un feed se representa con la clase
FeedConfig. Por el momento,la propia clase RSSReader es la que
lee/almacena las configuraciones.
• En el prototipo inicial, la configuración de cada feed se
guarda en un fichero.properties distinto, pero es de esperar que en
la versión definitiva elalmacenamiento de la configuración cambie a
un formato más complejo, como unsolo archivo XML.
• Para simplificar el ejercicio, se implementa la parte que lee
las configuraciones, perono la que las guarda.
Se pide:
• Revisar el diseño de la aplicación introduciendo los patrones
que se considerenadecuados. No hay por qué aplicar todos los vistos
en la charla.
• Implementar dichos cambios en el código de la aplicación. Se
recomienda hacer laimplementación de manera iterativa. Es decir,
introducir los patrones de uno en uno,asegurándose de que todo
funciona tras el cambio. NOTA: no es necesarioimplementar la
configuración en XML, solo dejar el código preparado para que
sepueda cambiar a XML de manera sencilla.
Patrones de diseño
15Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
3. Charla 2: Patrones para las capas de presentación y
negocio
En esta charla trataremos patrones para aplicaciones web basadas
en servlets y JSP. En laprimera charla ya vimos algunos ampliamente
usados en este tipo de aplicaciones: DAOs,Transfer Objects,
façades, etc. Estos patrones se encuadran dentro de las capas de
accesoa datos (DAO y TO) y negocio (TO y façade). Aquí veremos el
primer patrón propio de lacapa de presentación: el
Modelo-Vista-Controlador (MVC). Antes de presentarlo haremosun
breve repaso a las distintas alternativas de diseño para la capa de
presentación en unaaplicación web.
3.1. Arquitecturas para la capa de presentación en aplicaciones
web
Teniendo en cuenta que servlets y JSP son equivalentes desde el
punto de vista funcionaly que es mucho menos tedioso desarrollar un
interfaz web en JSP que en servlets, lamayoría de aplicaciones web
suelen usar JSPs para la capa de presentación o unacombinación de
JSPs y servlets. Veamos cuáles son las opciones a la hora de
diseñar laarquitectura de la capa de presentación.
3.1.1. Aplicaciones JSP-céntricas
En estas aplicaciones cada página JSP contiene todo el
procesamiento asociado a un casode uso (por ejemplo hacer login en
la aplicación) o de parte de un caso de uso (porejemplo, una de las
páginas del proceso de registro de nuevo usuario). Lo más
inmediatoa la hora de programar este tipo de aplicación es lo que
podríamos llamar la "ausencia dearquitectura": poner todo el código
de presentación, lógica de negocio y acceso a datosdentro de los
JSPs. En aplicaciones pequeñas resulta una manera sencilla de
"estructurar"el sistema, pero por razones evidentes en aplicaciones
medianas o grandes esto seconvierte en una pesadilla de desarrollo
y mantenimiento .
Las especificaciones iniciales de JSP hablaban de solucionar los
problemas anteriores conlo que Sun llamó el "modelo 1" de
arquitectura para aplicaciones web. La idea básica esque el código
de negocio y acceso a datos se "saca fuera" de los JSPs de modo que
éstosse encargan únicamente de la presentación. La propuesta
original habla de usar JavaBeanscomo encargados de "disparar" la
lógica de negocio y el acceso a datos porque su uso ysintaxis son
sencillos y son el modelo de componentes "estándar" en Java.
Combinandoesto con el uso de taglibs propias o estándar (tipo JSTL)
se puede eliminar el código Javadel JSP de modo que incluso
diseñadores web no programadores podrían en teoríaocuparse en su
totalidad de los JSP.
Patrones de diseño
16Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Arquitectura 'modelo 1'
Nota:El modelo 1 se conoce en el catálogo de Core J2EE patterns
como "View helper"
3.1.2. Combinación de servlets y JSP
Con la arquitectura anterior, el JSP no se dedica únicamente a
presentar los resultados,sino que tiene más responsabilidades. Una
alternativa es que para cada caso de uso hayaun servlet distinto
que se encargue de tomar los parámetros de la petición, llamar a
lalógica de negocio y colocar los resultados en algún ámbito que el
JSP pueda consultar(porejemplo en la petición o en la sesión). El
JSP se puede limitar a mostrar los resultadosobtenidos.
Patrones de diseño
17Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Combinación de servlets y JSP
Aunque parezca que el flujo de ejecución se complica, en
realidad se realizan las mismastareas, solo que en un orden
ligeramente diferente y lo más importante:
separandoresponsabilidades.
1. Un servlet recibe la petición HTTP del cliente y analiza los
parámetros2. El servlet activado dispara la lógica de negocio
llamando al/los JavaBean/s
correspondiente/s3. Los JavaBeans se encargan de ejecutar la
lógica de negocio y el acceso a datos4. El servlet coloca los
JavaBeans en algún ámbito al que un JSP pueda acceder.
Típicamente se usaría el objeto request. Después llama al JSP
apropiado.5. El JSP llamado, accede al JavaBean y muestra la
información que contieneEste modelo presenta la ventaja con
respecto al anterior de que el código del JSP sesimplifica todavía
más. Estamos solo a un paso de la arquitectura más usada en
laactualidad para aplicaciones web: Modelo-Vista-Controlador (MVC),
que veremos acontinuación.
3.2. Modelo-Vista-Controlador (MVC)
Esta arquitectura se parece mucho a la combinación de servlets y
JSPs que hemos vistoantes, con la única diferencia de que solo hay
un servlet, que es el que recibe todas laspeticiones. ¿Cómo sabe
entonces el servlet qué lógica de negocio hay que disparar y
qué
Patrones de diseño
18Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
JSP es el apropiado para mostrar los resultados?. Esto se puede
hacer por ejemplopasándole en la petición HTTP algún parámetro
adicional que lo indique, como:
http://.../aplicacionMVC/miUnicoServlet?accion=login&user=javaee&password=javaee
En este ejemplo el parámetro accion es el que le indica al
servlet qué es lo que hay quehacer. Los demás parámetros se
necesitan para poder ejecutar la operación, aquí hacerlogin en el
sistema. Luego veremos que hay otras alternativas (más usadas) a la
hora dedecirle al servlet qué operación hay que ejecutar.
3.2.1. Los componentes de la arquitectura: el modelo, la vista y
el controlador
Veamos de nuevo la arquitectura, pero ahora usando la
terminología habitual de MVC:
• El controlador (en nuestro caso el servlet) es el encargado
de:
1. Averiguar cuál es la operación a ejecutar2. Tomar los
parámetros de entrada y disparar la lógica de negocio apropiada
pasándole
dichos parámetros.3. Colocar los resultados de la operación en
algún ámbito accesible a un JSP
(normalmente la petición)4. Redirigir al navegador al JSP
apropiado• El modelo es la lógica de negocio y el código de acceso
a datos. Aunque hasta ahora
hemos hablado de JavaBeans, esto era una sugerencia de Sun para
simplificar elinterfaz con el JSP. El modelo puede ser en realidad
cualquier cosa: EJBs(componentes de negocio distribuidos), clases
Java convencionales, JavaBeans, etc.
• Las acciones son las operaciones a ejecutar para cada caso de
uso de la aplicación.Podemos imaginarnos que cada acción encapsula
de alguna forma todo elprocesamiento asociado a un caso de uso (o a
parte de un caso de uso, dependiendo decómo modelemos el sistema).
Las acciones se suelen considerar como parte delmodelo, aunque en
realidad están en la frontera entre éste y el controlador.
• La vista es el elemento encargado de mostrar los resultados
obtenidos en el modelo.En nuestro caso, son los JSPs
Nótese que cada petición HTTP implica una acción distinta y una
vista distinta, mientrasque solo hay un controlador y un modelo,
aunque cada petición requerirá partes distintasdel mismo: por
ejemplo para buscar productos en el catálogo hará falta ejecutar
distintalógica y acceder a distintos JavaBeans que para registrarse
en la web.
El diagrama de una posible arquitectura MVC (luego veremos que
hay variantes) semuestra en la siguiente figura. Nótese que es muy
parecido al de la combinación deservlets y JSPs que veíamos antes,
con la única diferencia de la terminología y que solohay un
servlet.
Patrones de diseño
19Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Modelo-Vista-Controlador
Nota:El patrón de diseño MVC no es ni mucho menos exclusivo de
aplicaciones web. Ni siquiera esoriginal del GoF. Fue descrito por
primera vez en 1979, con el nacimiento del lenguaje Smalltalken
Xerox, y hoy es ampliamente usado en multitud de frameworks,
librerías y entornos GUI (sinir más lejos la librería Swing de
Java). La web es solo uno de los contextos en los que se haaplicado
hasta ahora la idea de MVC, y es importante tener en cuenta que el
patrón tal y como seaplica en la web es una adaptación del patrón
original al funcionamiento de HTTP.
3.2.2. Una implementación de MVC
Al ser éste un patrón complejo, tener una implementación
concreta ayuda a comprender elfuncionamiento. Describiremos a
continuación una implementación sencilla perototalmente funcional
de MVC.
Aviso:En una aplicación real, no es recomendable la estrategia
de "reinventar la rueda". En JavaEE haydisponibles multitud de
frameworks MVC de probada calidad, de los cuales el estándar de
factoes Struts. Con un framework nos aseguramos de poder usar
código e ideas ya probadas, nos serámás fácil encontrar
desarrolladores ya preparados para trabajar y podremos aprovechar
lasmuchas herramientas existentes para facilitar el desarrollo (en
forma de plugins para Eclipse yotros IDES).
Patrones de diseño
20Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
3.2.2.1. El controlador
En nuestro caso el controlador se implementa mediante un
servlet. Esto es bastante típicode la mayoría de frameworks
MVC.
Configuración del web.xml
El primer paso a realizar es configurar el descriptor de la
aplicación (web.xml) para quelas peticiones con determinado patrón
vayan todas a parar al controlador. Así, todas laspeticiones con un
path común (por ejemplo /mvc/*) o con una extensión común
(porejemplo, en Struts se suele usar *.do) irán a dicho servlet,
que a partir de la URL decidecuál es la acción a disparar. En
nuestro caso, el servlet se mapeará a todas las peticiones*.mvc
Controladormvc.controlador.Controlador
Controlador*.mvc
Un refinamiento adicional sería restringir el acceso directo a
todas las páginas del sitio,para que solo fueran accesibles a
través del controlador. Esto se podría hacer por ejemplometiendo
todas las páginas en un directorio protegido y especificando
mediante unaetiqueta su condición de acceso restringido. No será
necesarioespecificar ningún si no se desea acceso directo por parte
de ningún usuario(en cualquier caso dicho acceso será posible a
través del servlet). Una alternativa a larestricción de ciertas
URLs sería colocar la web dentro de WEB-INF, que no esdirectamente
accesible desde fuera del contenedor web. Hay que tener en cuenta
que si seutiliza la restricción de acceso no será posible poner
enlaces en las páginas de la formahabitual. Un simple salto entre
páginas tendrá que ser ejecutado como una acción a travésdel
servlet controlador. Esto según se mire puede ser tedioso (ya que
complicamos unaoperación sencilla) o beneficioso (ya que abstraemos
la URL de la página destino, lo quehace posible el cambio sin que
por ejemplo queden afectados los bookmarks del usuario).
Las acciones
En el init() del servlet hay que crear la lista de acciones que
el controlador puede ejecutar,y asignarle a cada una un nombre
simbólico. En nuestro caso la lista de acciones está fijaen el
código para simplificar el ejemplo, aunque lo más razonable sería
mantenerla en unfichero aparte, por ejemplo en formato XML.
Patrones de diseño
21Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
public void init () throws ServletException {acciones = new
HashMap();acciones.put("prueba", new
AccionPrueba());acciones.put("login", new AccionLogin());...
}
Cada acción no es más que una instancia de una clase que se
disparará (ejecutando ciertométodo) a petición del controlador.
Procesamiento de las peticiones HTTP. En nuestrocaso, una acción
concreta es una clase que debe heredar de la clase abstracta
Accion, y eldisparo se realiza llamando al método ejecutar.
Ciclo de procesamiento
El ciclo de procesamiento de las peticiones es el siguiente:
1. A partir de la URL se obtiene el nombre simbólico de la
acción a ejecutar. En nuestrocaso es el nombre del "documento"
solicitado, sin la extensión .mvc
2. A partir del nombre simbólico de la acción, se obtiene una
instancia de la acción aejecutar. En nuestro caso esto es sencillo,
ya que se guarda una instancia de cadaacción en una tabla hash
accesible por nombre.
3. Se ejecuta la acción, llamando a un método. En nuestro caso
todas las accionesimplementan el interface
mvc.modelo.acciones.Accion, que obliga a definir unmétodo ejecutar.
Para que la acción pueda cumplir su tarea habrá que pasarle
lainformación contenida en el HttpServletRequest y probablemente
también elHttpServletResponse. Frameworks MVC más sofisticados
probablemente pasen estainformación como un objeto independiente
del API de servlets, habiendo copiadoantes a él la información
relevante, con el objeto de inpendendizar las acciones de lacapa
HTTP.
4. La acción coloca el resultado obtenido en algún sitio al que
pueda acceder la vista(por ejemplo, como un atributo en el ámbito
de la petición) y devuelve un valor que lesirve al controlador para
determinar la siguiente vista a mostrar. En nuestro caso, laacción
devuelve directamente el nombre del JSP a mostrar. En un caso más
realistadevolvería un nombre simbólico que serviría para determinar
el nombre real de lavista (por ejemplo, a través de un fichero de
configuración).
Nota:El encapsulamiento de los datos de la petición y la
respuesta en un objeto independiente delprotocolo HTTP es un patrón
de diseño que aparece en el catálogo de Core J2EE Patterns con
elnombre de Context Object. En nuestro caso, la aplicación de dicho
patrón serviría paraindependizar las acciones del protocolo HTTP,
lo que permitiría su reutilización para clientes noHTTP, como por
ejemplo clientes ricos Swing.
Aquí solamente mostramos el esqueleto del procesamiento de
peticiones, para ver cómose hace cada parte habrá que acudir al
código fuente completo.
Patrones de diseño
22Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
public void doPost (HttpServletRequest
request,HttpServletResponse response)
throws ServletException, IOException {String nomAccion;Accion
ac;String nomVista = null;
try {//obtener de la URL el nombre de la acciónnomAccion =
obtNomAccion(request);//a partir del nombre, obtener la clase
asociada a la
acciónac = getAccion(nomAccion);//ejecutar la accionnomVista =
ac.ejecutar(getServletContext(), request,
response);//mostrar la vista asociada a la accionif
(nomVista!=null)
mostrarVista(nomVista, request, response);}catch(MVCException e)
{
request.setAttribute("exception", e);mostrarVista(VISTA_ERROR,
request, response);
}}
Nota:Esta implementación posiblemente deja "demasiadas
responsabilidades" en manos delcontrolador, que tiene que hacer
todo el trabajo de averiguar la acción a disparar, ejecutarla,
pasarel resultado a la vista, etc. En el catálogo de Core J2EE
Patterns se propone el patrón"Application Controller" que
encapsularía el mapeo de acciones y su ejecución, lo que dacomo
resultado una mayor modularidad del sistema y la posible
reutilización del "ApplicationController" si este se hace
independiente del protocolo de comunicación usando un
"ContextObject" (ver nota anterior).
3.2.2.2. El modelo
Este es el punto donde suele terminar la "responsabilidad" del
framework MVC yempieza el núcleo de nuestra aplicación.
Como ya hemos comentado, en nuestro caso las acciones
implementan el interfacemvc.modelo.acciones.Accion, que obliga a
definir el método ejecutar.En general, loprimero que hará una
acción es comprobar que los parámetros son del tipo esperado
ycumplen una serie de normas de validación (por ejemplo,
determinado formato para datostextuales, o estar en un rango para
valores numéricos). La mayor parte de frameworksMVC proporcionan
medios para que el usuario defina "validadores" de manera más
omenos sencilla y configurable. Una vez la acción ha terminado su
trabajo, debe colocarlos resultados en algún lugar accesible a la
vista que debe mostrarlos. En nuestro caso, losalmacenaremos como
atributos de la petición HTTP. Como el controlador se redirige
alJSP que hace de vista con un forward, este tendrá acceso a dichos
atributos.
Patrones de diseño
23Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Las acciones lo que harán será modificar y/o obtener el estado
del sistema. Dicho estadoestará representado por un modelo
orientado a objetos del dominio, en el cualrepresentaremos las
entidades de nuestra aplicación. La "lógica de negocio" son
losprocesos a realizar para cumplimentar alguna operación en el
sistema. En una primeraaproximación es tentador introducir toda
esta lógica dentro del cuerpo de las acciones.Así, por ejemplo, la
acción RealizarPedido dentro de su método ejecutar podría
chequearque el pedido es válido y el cliente no es moroso, aplicar
el descuento apropiado al tipo decliente y plazo de pago e
introducir el pedido en la base de datos mediante JDBC. Noobstante,
hay que tener en cuenta que en algunos frameworks MVC las acciones
están"acopladas" a la capa web (por ejemplo en Struts) y por tanto
no son el lugar apropiadopara introducir dicha lógica, que debería
ser lo más reutilizable posible para otros clientes(por ejemplo,
"clientes ricos" Swing).
3.2.2.3. La vista
En nuestro caso una vista será simplemente una página JSP que se
limitará a mostrar losdatos procesados por la acción. Idealmente,
dichos datos estarán encapsulados en beansaccesibles a través del
ámbito de la petición,con lo que el código Java a introducir
dentrode la vista se reducirá al mínimo
3.2.3. Variantes de MVC
Siendo un patrón complejo, MVC admite multitud de variantes de
funcionamiento. Noobstante, nos vamos a centrar aquí en diferenciar
dos tipos de MVC: push y pull. Elesquema que hemos visto hasta el
momento es de tipo push, ya que la vista recibe losresultados de la
ejecución de la acción, ella no los dispara. En el catálogo de Core
J2EEpatterns esto es lo que se conoce como el patrón "Service to
worker". Un ejemplo real deimplementación de este patrón es el
framework Struts.
Una alternativa a este modelo sería el modelo pull, en el cual
la vista es la responsable dedisparar la acción. En el catálogo de
Core J2EE patterns esto es lo que se conoce como elpatrón
"Dispatcher view". Un ejemplo real de framework que usa este
enfoque esJavaServer Faces (JSF), que aunque se conoce básicamente
como un modelo decomponentes GUI para la web también incluye un
modelo MVC para el desarrollo de lasaplicaciones. En JSF, cuando se
"dibuja" la página es cuando los beans asociados a lamisma disparan
la lógica de negocio.
3.3. Algunos patrones para la capa de negocio
Para terminar esta discusión introduciremos brevemente algunos
patrones para la capa denegocio. La función de muchos de estos
patrones es proporcionar una capa de abstracciónsobre la
implementación del sistema, de modo que por ejemplo no importe si
laaplicación es local o distribuida, implementada con EJB3 o EJB2,
... Como en el cursotodavía no hemos visto en la práctica más que
un modelo de arquitectura, el de las
Patrones de diseño
24Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
aplicaciones locales, haremos una primera aproximación a estos
patrones "a vista depájaro" y volveremos sobre ellos en la última
charla del módulo.
En general no es interesante que los servlets/JSP o las acciones
si usamos MVC llamendirectamente a la capa de acceso a datos (los
DAOs), por varias razones:
• Estamos introduciendo lógica de negocio en la capa de
presentación, lo que nopermite reutilizar esta lógica si usamos
otro tipo de cliente (por ejemplo un clienterico basado en
Swing)
• Los servlets/JSPs/acciones dependen del interfaz de las clases
a las que llaman. Nopodemos cambiar el interfaz de los DAOs sin que
este cambio repercuta en la capa depresentación.
Para solucionar estos y otros problemas, en las aplicaciones
JavaEE se pueden introducirdistintos patrones.
3.3.1. Business Object
Un business object no es más que un objeto del dominio de la
aplicación. En el caso de labiblioteca del proyecto de integración
tendríamos probablemente Usuario, Libro yOperacion. La diferencia
entre un business object y un transfer object es que el
primeroimplementará algo de lógica de negocio, mientras que el
segundo es un mero "paquete dedatos" que viaja de un extremo a otro
de las capas de la aplicación.
Las aplicaciones sencillas pueden funcionar sin un modelo del
dominio, es decir, sinbusiness objects. En este caso, los propios
TO desempeñarían el papel (aunque de modorudimentario) de modelo de
dominio. Este es precisamente el estadio en que se encuentraahora
nuestra aplicación de biblioteca.
3.3.2. Application Service
La misión fundamental de este patrón es encapsular lógica de
negocio, sobre todo la queimplica a más de un objeto. Por ejemplo,
en el caso de la biblioteca, la operación deprestar un libro no se
debe implementar en la clase Operacion, ya que no solo involucra
aesta sino también a Usuario (hay que comprobar si un usuario es
moroso antes de realizarel préstamo).
El application service se suele apoyar en DAOs y business
objects para realizar su labor.
3.3.3. Session Façade
La misión fundamental de este patrón es la de actuar como
fachada de la capa de negocioen aplicaciones distribuidas. Por ello
lo abordaremos en profundidad cuando en el cursohayamos trabajado
con este tipo de aplicaciones.
El session façade es cliente del Application Service o bien de
los business objects si éste
Patrones de diseño
25Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
no existe.
3.3.4. Business Delegate
Este patrón constituye el "punto de entrada" a la capa de
negocio desde la capa depresentación. La misión de este patrón es
aislar a la capa de presentación de la de negocioen dos
aspectos:
• En aplicaciones distribuidas, aislar de los detalles de la
comunicación remota.• En aplicaciones en las que la implementación
de la capa de negocio pueda cambiar de
un "paradigma" a otro, aislar del API de negocio. Por ejemplo,
un cambio de EJB 2.0a EJB3.
Como todavía no hemos visto en el curso aplicaciones
distribuídas ni tecnologías para laimplementación de componentes de
negocio, dejaremos la discusión de estos aspectospara más
adelante.
El business delegate es cliente del session façade.
Como se ve, estos patrones se apoyan unos en otros, generando
diversas capas que por unlado aportan flexibilidad y
encapsulamiento a nuestra arquitectura pero por otro
añadencomplejidad. La experiencia y el sentido común nos dirán en
cada caso qué patrones sonnecesarios en nuestro proyecto y cuáles
podemos obviar, si los beneficios que aportan nocompensan la
complejidad de su implementación y uso.
4. Ejercicio de la charla 2: Patrones para aplicaciones web
Usando como contexto la aplicación incluida en las plantillas de
la sesión, vamos aimplementar un par de acciones MVC para captar
mejor todas sus implicaciones.
4.1. La aplicación de bookmarks
En las plantillas de la sesión se incluye una aplicación muy
sencilla para gestionarbookmarks on line.
La aplicación está dividida en dos proyectos: el proyecto java
Bookmarks, con las capasde negocio y acceso a datos, y el proyecto
web WebBookmarks, con la implementación deMVC "casera" vista en las
transparencias.
Para empezar a trabajar con la aplicación lo primero es crear la
base de datos. En lacarpeta "db" se incluye un script de ant para
ello.
Para comprobar la aplicación, ejecutar el JSP test/testDAO.jsp.
Debería aparecer unalista con los bookmarks que hay actualmente en
la base de datos.
Patrones de diseño
26Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
4.2. Implementación de un caso de uso con MVC
Implementa el caso de uso "listar bookmarks" usando
Modelo-Vista-Controlador. Paraello, en el proyecto web tendrás
que:
• Definir una nueva clase jtech.mvc.acciones.AccionListar, que
implemente elinterface IAccion. En dicha clase debes implementar la
operación de listarbookmarks:1. Llamando al método listarBookmarks
del DAO IBookmarkDAO. La instancia de
este se obtiene a través de la FactoriaDAOs. Puedes mirar el
código del JSPtest/testDAO.jsp.
2. Colocando en el request el List obtenido del DAO.3.
devolviendo lista como resultado de ejecutar la acción.
• Añadir la clase AccionListar al Hashmap del servlet
controlador.• Implementar la página lista.jsp para que, haciendo
uso de JSTL, se muestre la lista
de bookmarks. Puedes mirar el código del JSP test/testDAO.jsp.
Fíjate que en elJSP no debería aparecer nada de código java, solo
JSTL y expresiones en EL (${...}).
4.3. Caso de uso "crear nuevo bookmark" (*)
Implementa el caso de uso "crear nuevo bookmark". Necesitarás un
HTML con unformulario para introducir los datos del nuevo bookmark
y una acción que recoja esosdatos y cree el bookmark llamando al
DAO.
Patrones de diseño
27Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
5. Charla 3: Patrones para aplicaciones enterprise
Como sabe perfectamente Peter Parker, "un gran poder conlleva
una granresponsabilidad". El desarrollador de aplicaciones
enterprise con Java EE tiene a sudisposición un gran número de
herramientas y tecnologías para diseñar e implementaraplicaciones a
gran escala. Precisamente esta escala es la que hace que
usaradecuadamente dichas tecnologías sea más crítico si cabe que en
el caso de aplicacionesweb, que suelen tener un tamaño menor y una
arquitectura más simple. Los patronesenterprise van a ayudarnos
dándonos una guía de "buenas prácticas" específicas para estetipo
de aplicaciones.
5.1. Patrones para aplicaciones distribuidas vs. aplicaciones
"locales"
Además de la escala, dos son las características fundamentales
de una aplicaciónenterprise frente a una aplicación web
"convencional"
• La posibilidad de distribuir la aplicación en varias máquinas
físicas y/o lógicas• El uso de los servicios proporcionados por un
servidor de aplicaciones, como JNDI o
EJBs
De las dos, la que tiene un impacto mayor a priori sobre el
rendimiento es la primera:estar ejecutando llamadas remotas entre
objetos a través de la red incurre en costes que, sibien tomados de
modo individual son pequeños (del orden de milisegundos) si no
segestionan eficientemente pueden dar lugar a enormes "cuellos de
botella". Algunos de lospatrones que veremos van destinados
precisamente a solucionar este problema: organizarla aplicación de
un modo eficiente para que el coste extra de las llamadas remotas
tenga elmenor impacto posible sobre el rendimiento. El caso
prototípico es el del patrón sessionfaçade
Por otro lado, la "herramienta" paradigmática para el desarrollo
de aplicacionesdistribuidas en Java EE son los EJBs. Hasta la
aparición de la versión 3 de laespecificación, los EJBs eran muy
potentes pero también poco eficientes y de unacomplejidad
considerable para el programador. Aparecieron diversos patrones
nacidosúnicamente para ocultar y compartimentar la complejidad de
los EJBs y para minimizar elimpacto en la eficiencia. Es decir,
eran patrones que venían a "parchear" los defectos dela tecnología.
La versión 3 de los EJBs alivia la situación, dando la misma
potencia conun uso mucho más sencillo. Esto también hace que muchos
de los patrones antiguos ya nosean necesarios o al menos no tan
críticos como lo fueron en el pasado. La discusión quesigue
supondrá que estamos usando la versión 5 de JavaEE o superior.
Podemos consultarla bibliografía para información sobre cómo serían
los patrones si en un proyecto nosviéramos obligados a usar EJB
2.x.
5.2. Fuentes bibliográficas para patrones enterprise
Patrones de diseño
28Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Parte del interés de un patrón radica en su "reconocimiento" por
parte de la comunidad.Un patrón no solo debe resolver un problema:
además es una "pieza" que otrosdesarrolladores deberían reconocer
al instante al verla aparecer en un diseño. Es obvioque el
vocabulario común facilita la documentación y la comunicación entre
el equipo detrabajo. Es por ello que en patrones se vuelve vital el
conocimiento de las fuentesbibliográficas más "influyentes". Y es
también por ello que no relegamos la bibliografíaal sitio habitual,
que suele ser el final de los temas.
Ya vimos en la primera charla que el trabajo más conocido en
patrones de diseñogenéricos era el del Gang of Four (GoF). En el
contexto de aplicaciones enterprise hansurgido en los últimos años
varios trabajos influyentes. Algunos de ellos son aplicables
acualquier plataforma tecnológica, mientras que otros son
específicos para Java Enterprise:
• Patterns of Enterprise Application Architecture, Martin
Fowler, Addison Wesley:aplicable a cualquier aplicación enterprise,
independientemente de la plataforma deimplementación. El libro
incluye ejemplos en Java y C++. Los patrones puedenconsultarse
online en el sitio de Martin Fowler, aunque en la web solo se pone
unabreve explicación, mucho más corta que la discusión en
profundidad del libro.
• Core J2EE Patterns, Alur, Crupi & Malks: patrones
específicos para Java EE. Enrealidad, son propios de la "época"
pre-Java EE 5, por lo que no incluyen nada de laúltima revolución
en la plataforma. Por ello, aunque interesante, el libro se
haquedado anticuado. Procede de un tiempo en el que la tecnología
tenía un montón deinconvenientes y problemas que era necesario
evitar usando ciertos trucos y siguiendoformas muy específicas de
hacer las cosas.
• Sun dispone de un sitio web llamado "Java Blueprints". En
tiempos fue la fuente dereferencia fundamental de "buenas
prácticas". Hoy en día no se actualiza al mismoritmo que hace unos
años, aunque se van incorporando poco a poco patrones quetienen en
cuenta la versión 5 de Java EE.
5.3. Minimizando las llamadas remotas: Session Facade
5.3.1. Problema
Ejemplo: una aplicación que permite enviar mensajes entre
usuarios. La aplicación esdistribuida y la capa web está en una
máquina distinta a la capa de negocio. La capa webestá implementada
con Struts y la de negocio con EJBs de sesión y DAOs JDBC. Como
alos EJBs se puede acceder desde cualquier objeto Java buscándolos
con JNDI, la acciónde Struts podría acceder directamente a todos
los EJBs necesarios para un caso de uso. Enla figura se muestra
cómo funcionaría el caso "enviar mensaje", que requiere de
lacolaboración de dos EJB, para el envío del mensaje (MensajeBO) y
para el cobro delenvío al usuario (UsuarioBO).
Patrones de diseño
29Copyright © 2007-2008 Depto. CCIA All rights reserved.
http://www.martinfowler.com
-
Acceso directo a los objetos del dominio
Esta forma de trabajar es problemática por varias razones:
• Desde el punto de vista lógico: ya hemos visto en módulos
anteriores que una acciónde Struts, o cualquier otro tipo de
cliente de la capa de negocio no debe implementarnada de lógica de
negocio. En el ejemplo anterior incumplimos la norma, ya que
laacción está "viendo" la estructura interna del caso de uso.
• Desde el punto de vista de costes: un caso de uso implica
normalmente a más de unbean de entidad. Si el cliente tiene que
acceder de manera remota a cada uno de estosbeans de entidad, debe
hacer múltiples llamadas a través de RMI con el alto coste queesto
conlleva.
• Desde el punto de vista de la integridad transaccional: los
EJBs pueden gestionar deforma automática la transaccionalidad, pero
cada operación con un bean distinto seejecutaría en una transacción
separada. Podríamos usar JTA para agruparlo todo demanera manual,
pero entonces se perdería parte del beneficio de los EJBs: el
manejoautomático y declarativo de transacciones.
Por tanto necesitamos algún mecanismo que nos permita: reducir
la comunicación porla red, colocar la lógica de negocio de forma
coherente y agrupar las operaciones enuna única transacción
declarativa.
5.3.2. Solución
Utilizar el patrón Session Facade. En este, los EJBs de sesión
forman el "frontal" de lacapa de negocio, ofreciendo un API de
métodos al que pueden acceder los clientes.
Uso de session façade
El session façade es un EJB de sesión para poder aprovechar los
beneficios que nos danlos EJBs: acceso remoto transparente,
transaccionalidad y seguridad declarativas ygestión automática del
ciclo de vida.
Independientemente de la mejora de eficiencia, puede verse que
este patrón es unaimplementación mediante un EJB de sesión del
Facade del GoF, que se usa también en
Patrones de diseño
30Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
aplicaciones sin EJBs para centralizar la lógica de negocio.La
única diferencia deimplementación es que en aplicaciones
convencionales la fachada es un POJO local yaquí, como hemos visto,
es un EJB de sesión remoto.
Un problema práctico es cuántos Session Façades hay que definir
en una aplicación. Lomás sencillo es definir uno solo, que
contendría todo el API de la capa de negocio, peroeste enfoque
lleva a clases con demasiado código no relacionado entre sí. La
prácticacomún es agrupar los casos de uso relacionados (que actúan
sobre las mismas entidades)en un Session Facade propio.
5.3.3. Posibles problemas
Como consecuencias negativas, un uso inadecuado del patrón puede
llevar a:
• Crear beans de sesión con mucho código y difíciles de
gestionar si no se tiene unadisciplina clara para separar casos de
uso.
• Dar demasiadas competencias al Session Facade, colocándole la
lógica interna deobjetos del dominio, que éstos deberían manejar
por sí mismos.
• Duplicación de código común a varios casos de uso. Cuando se
detecte este códigodebe sacarse fuera del Session Facade y
colocarse en POJOs en la capa de negocio,formando un patrón
Application Service, es decir, una capa auxiliar que contienelógica
de negocio.
5.4. Transfiriendo datos a los clientes: Transfer Object
5.4.1. Problema
Ejemplo: una acción de Struts accede de forma remota a un EJB
con los datos de unpedido. Cada llamada a un getter del EJB es en
realidad una llamada remota, lo cual esclaramente ineficiente. No
obstante necesitamos transferir de algún modo los datos delpedido
desde negocio hacia presentación. Buscamos por tanto la manera
menos costosade transferir información entre capas que residen en
máquinas distintas.
5.4.2. Solución
Realizar la transferencia de información entre capas empaquetada
en objetos del tipoDTO, Data Transfer Objects, que en su versión
más sencilla son simplemente JavaBeans.Las operaciones serán mucho
más eficientes con una única llamada que transfiera unpaquete de
información en lugar de con múltiples llamadas con datos
individuales. Porsupuesto, los DTOs también se usan en aplicaciones
no distribuidas, pero en este caso suuso está más justificado por
cuestiones de organización de código (1 parámetro en lugarde
varios) que por la eficiencia.
Aquí es donde podemos ver la justificación de por qué los DTOs
deben ser serializables:si la aplicación pasa de ser local a
distribuida, los DTOs van a viajar entre distintas JVMs
Patrones de diseño
31Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
. Aunque esta condición no sería estrictamente necesaria para
una aplicación local, esmuy fácil de seguir desde el principio y
nos protege ante posibles cambios en laarquitectura.
5.4.3. Posibles problemas
Para muchos desarrolladores, los DTOs son un "mal necesario".
Son objetos sincomportamiento, solo con estado, lo que los hace más
parecidos a un struct de C que averdaderos objetos. Irónicamente,
algunos dicen que cuando se usan DTOs lo que se tienees un "modelo
del dominio con anemia" (anemic domain model). No obstante, con
losAPIs de hace unos años eran la única forma eficiente de
transferir datos entre capas. Laaparición de JPA ha hecho posible
usar entidades persistentes con estado ycomportamiento y mover
dichos objetos a través de todas las capas de la aplicación:desde
el almacén de datos a la presentación. Por eso el DTO es
considerado en laactualidad por algunos como un patrón y por otros
como un "antipatrón" o una formaincorrecta de hacer las cosas.
5.5. Localizando servicios: Service Locator
5.5.1. Problema
Supongamos que una acción de Struts necesita acceder a un EJB
remoto. En JavaEE 5 elcontenedor nos da acceso "automático" a los
EJBs y otros recursos mediante la inyecciónde dependencias. Pero
esta inyección de dependencias solo es aplicable a
objetosgestionados por el contenedor, lo que no es el caso de una
acción de Struts. Por tantonecesitamos usar el API JNDI para
localizar el EJB. Tendremos que usar un códigosimilar al
siguiente:
Properties props = new
Properties();props.setProperty(...)props.setProperty(...)...InitialContext
ic = new InitialContext(props);UsuarioBORemote ubo;ubo =
(UsuarioBORemote) ic.lookup("jtech.UsuarioBORemote");
No solo esta acción, todas las clases no gestionadas por el
contenedor que necesiten deEJBs o de otros recursos del servidor de
aplicaciones necesitan de un código equivalente.No es excesivamente
complicado, pero es tedioso y repetitivo. Además parte del
códigoJNDI es dependiente del servidor, por lo que conviene
aislarlo del resto del código parafacilitar la portabilidad. En
concreto el Properties necesario para crear elInitialContext
depende del servidor de aplicaciones y también el propio nombre
JNDI(en el ejemplo se ha usado la convención de Glassfish, que es
poner el nombre completode la clase, pero en otros servidores no es
así) . Nuestro objetivo es proporcionar un"responsable
centralizado" para localización de recursos JNDI.
Patrones de diseño
32Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
5.5.2. Solución
Utilizar el patrón service locator para implementar y encapsular
la búsqueda de recursos.El service locator esconde al cliente las
posibles complejidades de la búsqueda,proporciona un API uniforme y
puede mantener una cache para evitar llamadasinnecesarias a JNDI.
Normalmente no hará falta más de un service locator para toda
laaplicación, por lo que se puede utilizar para su implementación
el patrón singleton delGoF.
La siguiente figura muestra el diagrama de clases del
patrón.
Service Locator
Como se ve, el Service Locator (que es un singleton) recibe
solicitudes de los clientespara localizar recursos JNDI. Al
inicializarse,el Service Locator crea un contexto inicialJNDI. Cada
vez que recibe una solicitud de un recurso JNDI hace uso de dicho
contexto.Mantiene además una Cache con los recursos ya solicitados
para evitar peticiones JNDIinnecesarias.
5.5.3. Consecuencias
Las consecuencias positivas son:
• Abstrae la complejidad: se encapsula el mecanismo de
localización de servicios demodo que el cliente no necesita conocer
los detalles.
• Proporciona un interfaz uniforme de acceso a recursos: que
pueden usar todos los
Patrones de diseño
33Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
clientes y que abstrae de las posibles características propias
de la implementaciónJNDI empleada.
• Mejora el funcionamiento de la aplicación: mediante la cache
se evitan llamadasinnecesarias a JNDI
Patrones relacionados
• Singleton (GoF): normalmente en una aplicación bastará con
tener un único ServiceLocator, por lo que este patrón se suele
implementar como un singleton.
• Data Access Object: un DAO normalmente utiliza un Service
Locator para acceder afuentes de datos JDBC.
• Business Delegate: en aplicaciones con componentes remotos
utiliza un ServiceLocator para localizar servicios.
5.6. Haciendo transparentes los EJBs de negocio:Business
Delegate
5.6.1. Problema
Siguiendo con los ejemplos anteriores: si una acción de Struts
necesita de un EJB,estamos haciendo su código dependiente de la
implementación de la capa de negocio.Aunque el service locator
abstrae los detalles de JNDI, sigue quedando claro que detráshay un
EJB. Si queremos reducir al mínimo el acoplamiento tenemos que
"ocultar" lanaturaleza de la capa de negocio.
//La clase de la capa de presentaciónpublic class AccionLogin
extends Action {public ActionForward execute(ActionMapping mapping,
ActionForm
form,HttpServletRequest req, HttpServletResponse resp){LoginForm
lf = (LoginForm) form;ServiceLocator sl = sl.getInstance();//se
conoce la implementación de la capa de negocioUserFacadeRemote uf
=
sl.getEJBResource("jtech.UserFacadeRemote");return
uf.login(login, password);...
}}
5.6.2. Solución
El business delegate: una fina "capa" entre presentación y
negocio que oculta a la primerala tecnología usada para la
implementación de la segunda. El BD es simplemente unPOJO. Aunque
desde el punto de vista lógico pertenece a la capa de negocio (y
como taldeben ocuparse de él los desarrolladores de esta capa),
desde el punto de vista físico sedebe colocar en la capa de
presentación para que las comunicaciones con los objetos deesta
última sean locales. Los clientes que solicitan sus servicios no
tienen por qué saber
Patrones de diseño
34Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
que estos son accedidos en realidad de forma remota.
//La clase de la capa de presentaciónpublic class AccionLogin
extends Action {public ActionForward execute(ActionMapping mapping,
ActionForm
form,HttpServletRequest req, HttpServletResponse resp){LoginForm
lf = (LoginForm) form;//Aquí habría que poner un factory,
mejorBUsuarios bdu = new BDUsuarios();//¿quién diría que detrás hay
EJBs remotos?UsuarioTO u = bdu.login(lf.getLogin(),
lf.getPassword());...
}}
//El business delegatepublic class BDUsuarios {public void
UsuarioTO login(String login, String password) {ServiceLocator sl =
sl.getInstance();//conoce la implementación de la capa de
negocioUserFacadeRemote uf =
sl.getEJBResource("jtech.UserFacadeRemote");return
uf.login(login, password);}
}
5.6.3. Posibles problemas
La transparencia en las llamadas remotas que ofrece el BD podría
causar un mal uso delpatrón: que los desarrolladores de la capa de
presentación olviden que los métodos denegocio son en realidad
remotos y acaben haciendo un uso ineficiente y poco optimizadode
las llamadas.
Por otro lado, el BD es una capa más, que introduce un nivel de
complejidad adicional.Dicho nivel de complejidad solo está
justificado en caso de que sea realmente necesarioreducir al mínimo
el acoplamiento entre presentación y negocio. Un ejemplo sería
unaaplicación que comienza siendo web, con una capa de negocio
basada en POJOs y luegopasa a usar EJBs. En ese caso el BD nos
permite no tener que cambiar el código de lacapa de
presentación.
6. Ejercicio de la charla 3: Patrones para aplicaciones
enterprise
Tenemos una aplicación con una pequeña "red social". En dicha
red los usuarios puedenenviarse mensajes unos a otros. Se trata de
refactorizar el caso de uso de "enviarmensaje", introduciendo
patrones que mejoren la eficiencia del sistema y la modularidaddel
diseño.
Patrones de diseño
35Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
6.1. Estructura de la aplicación
La aplicación está dividida en dos proyectos NetBeans: un
proyecto web basado en Strutsy un proyecto ejb con la capa de
negocio y de acceso a datos. Los dos proyectos están enlas
plantillas de la sesión. Además se incluye un script SQL con la
base de datos. Podéiscrear la BD con dicho script desde el programa
"MySQL query browser": Se abre el .sqlcon la opción File > Open
Script y se ejecuta con el botón "Execute" de la parte
superiorderecha de la pantalla. Creará una BD llamada "amigos" con
dos tablas.
Comprobad que la aplicación funciona correctamente desplegando
los dos proyectos enGlassFish. Primero desplegar el proyecto
"amigos-negocio" y luego "amigos-web".Probar que se puede enviar un
mensaje del usuario "maria" al usuario "pepe". Observadque como el
envío de mensaje tiene un coste de 2 unidades, habrá usuarios que
no puedanenviar el mensaje si no tienen crédito suficiente. La
aplicación no dará error pero si veisla BD observaréis que el
mensaje no se inserta en la tabla.
La acción de EnviarMensajeAccion debe interactuar con dos EJB de
la capa de negocio,los dos remotos:
• GestorMensajesRemote: el encargado del envío del mensaje, es
decir, de insertar elmensaje en la tabla "mensajes" con JPA.
• GestorUsuariosRemote: el encargado del cobro del mensaje. Si
el usuario tienecrédito suficiente se disminuye en dos unidades
Hay que tener en cuenta que:
• Como las acciones de Struts no son objetos gestionados por el
contenedor, debemosusar JNDI para acceder a los EJBs
• Si se envía el mensaje pero no hay crédito suficiente, se hace
un rollback de latransacción, usando JTA
6.2. Patrón fachada
Introducir el patrón fachada en la aplicación. Dicha fachada
será un nuevo EJB. La acciónEnviarMensaje únicamente comunicará con
este EJB, que será el encargado de a su vez"hablar" con el
GestorMensajes y con el GestorUsuarios
1. En el proyecto "amigos-negocio" crear un nuevo EJB de sesión
sin estado llamado"Fachada" (es decir, la clase de implementación
se llamará FachadaBean y el interfazremoto FachadaRemote). No
necesitamos interfaz local, ya que a la fachada solo seaccede desde
"fuera". El interfaz remoto tendrá dos métodos:
void enviarMensajeRemote(MensajeTO mto)void
getUsuarioRemote(String login)
Solo implementaremos el primero, el segundo puedes dejarlo vacío
en laimplementación.
Patrones de diseño
36Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
2. Traslada el código que creas necesario del "execute" de la
acciónEnviarMensajeAccion al "enviarMensajeRemote" de la fachada.
Ten en cuenta que laacción ahora solo debe "hablar" con la fachada.
La fachada es la que "hablará" con elGestorMensajes y el
GestorUsuarios. En la acción EnviarMensajeAccion sigue
siendonecesario JNDI para localizar la fachada pero la fachada
puede usar inyección dedependencias para comunicarse con los otros
dos EJBs
3. Quita el código JTA de la acción EnviarMensajeAccion. Ya no
es necesario, porquelas transacciones las va a gestionar la
fachada. En la fachada deberás detectar si se haproducido una
"SinCreditoException" y hacer un rollback como viste en la sesión
detransacciones del módulo EJB (context.setRollbackOnly()).
6.3. Patrón Business Delegate (*)
Introduce el patrón Business Delegate en la aplicación. Este
será una clase javaconvencional llamada precisamente
BusinessDelegate dentro del proyecto "amigos-web".El business
delegate debe contener todo el código JNDI y la llamada al EJB
fachada. Esdecir, la acción EnviarMensaje debe llamar al Business
Delegate que a su vez, medianteJNDI, localizará la fachada y
llamará a su método enviarMensajeRemoto. Lo más lógicoes que el
Business Delegate encapsule esta operación dentro de un método
llamadotambién "enviarMensaje"
Patrones de diseño
37Copyright © 2007-2008 Depto. CCIA All rights reserved.
-
Patrones de diseño
38Copyright © 2007-2008 Depto. CCIA All rights reserved.
1 Charla 1: Introducción a los patrones de diseño. Algunos
patrones básicos1.1 Por qué patrones1.2 Patrones J2EE1.3 Singleton
(GoF)1.3.1 Problema a resolver1.3.2 Discusión y beneficios1.3.3
Relación con otros patrones
1.4 Facade (GoF)1.4.1 Problema a resolver1.4.2 Discusión y
beneficios
1.5 Factory (GoF)1.5.1 Problema a resolver1.5.2 Simple
factory1.5.3 Factory method
2 Ejercicio de la charla 1: Introducción a los patrones de
diseño2.1 Refactorización basada en patrones
3 Charla 2: Patrones para las capas de presentación y negocio3.1
Arquitecturas para la capa de presentación en aplicaciones web3.1.1
Aplicaciones JSP-céntricas3.1.2 Combinación de servlets y JSP
3.2 Modelo-Vista-Controlador (MVC)3.2.1 Los componentes de la
arquitectura: el modelo, la vista y el controlador3.2.2 Una
implementación de MVC3.2.2.1 El controlador3.2.2.1.1 Configuración
del web.xml3.2.2.1.2 Las acciones3.2.2.1.3 Ciclo de
procesamiento
3.2.2.2 El modelo3.2.2.3 La vista
3.2.3 Variantes de MVC
3.3 Algunos patrones para la capa de negocio3.3.1 Business
Object3.3.2 Application Service3.3.3 Session Façade3.3.4 Business
Delegate
4 Ejercicio de la charla 2: Patrones para aplicaciones web4.1 La
aplicación de bookmarks4.2 Implementación de un caso de uso con
MVC4.3 Caso de uso "crear nuevo bookmark" (*)
5 Charla 3: Patrones para aplicaciones enterprise5.1 Patrones
para aplicaciones distribuidas vs. aplicaciones "locales"5.2
Fuentes bibliográficas para patrones enterprise5.3 Minimizando las
llamadas remotas: Session Facade5.3.1 Problema5.3.2 Solución5.3.3
Posibles problemas
5.4 Transfiriendo datos a los clientes: Transfer Object5.4.1
Problema5.4.2 Solución5.4.3 Posibles problemas
5.5 Localizando servicios: Service Locator5.5.1 Problema5.5.2
Solución5.5.3 Consecuencias
5.6 Haciendo transparentes los EJBs de negocio:Business
Delegate5.6.1 Problema5.6.2 Solución5.6.3 Posibles problemas
6 Ejercicio de la charla 3: Patrones para aplicaciones
enterprise6.1 Estructura de la aplicación6.2 Patrón fachada6.3
Patrón Business Delegate (*)