Composición y variación de patrones
Preámbulo
Los veintitrés patrones de diseño presentados en este libro no constituyen una lista exhaustiva. Es posible crear nuevos patrones bien ex nihilo, o bien componiendo o adaptando patrones existentes. Estos nuevos patrones pueden tener un carácter general, a semejanza de los que hemos presentado en los capítulos anteriores, o ser específicos a un entorno de desarrollo particular. De este modo podemos citar el patrón de diseño de los JavaBeans o los patrones de diseño de los EJB.
En este capítulo, vamos a mostrar tres nuevos patrones obtenidos mediante la composición y variación de patrones existentes.
El patrón Pluggable Factory
1. Introducción
Hemos presentado en un capítulo anterior el patrón Abstract Factory que permite abstraer la creación (instanciación) de productos de sus distintas familias. En este caso se crea una fábrica asociada a cada familia de productos. En el diagrama de la figura 5-1.1, se exponen dos productos: automóviles y scooters, descritos cada uno mediante una clase abstracta. Estos productos se organizan en dos familias: gasolina o electricidad. Cada una de las dos familias engendra una subclase concreta de cada clase de producto.
Existen por tanto dos fábricas para las familias FábricaVehículoGasolina y FábricaVehículoElectricidad. Cada fábrica permite crear uno de los dos productos mediante los métodos apropiados.
Este patrón organiza de forma muy estructurada la creación de objetos. Cada nueva familia de productos obliga a agregar una nueva fábrica y, por tanto, una nueva clase.
De forma opuesta, el patrón Prototype presentado en el capítulo del mismo nombre proporciona la posibilidad de crear nuevos objetos de manera muy flexible.
Figura 5-1.1 - Ejemplo de uso del patrón Abstract Factory
La estructura del patrón Prototype se describe en la figura 5-1.2. Un objeto inicializado y listo para ser utilizado con capacidad de duplicarse se llama un prototipo.
El cliente dispone de una lista de prototipos que puede duplicar cuando así lo desea. Esta lista se construye dinámicamente y puede modificarse en cualquier momento a lo largo de la ejecución. El cliente puede construir nuevos objetos sin conocer la jerarquía de clases de la que provienen.
Figura 5-1.2 - Ejemplo de uso del patrón Prototype
La idea del patrón Pluggable Factory consiste en componer estos dos patrones para conservar por un lado la idea de creación de un producto invocando un método de la fábrica y por otro lado la posibilidad de cambiar dinámicamente la familia que se quiere crear. De este modo, la fábrica no necesita conocer las familias de objetos, el número de familias puede ser diferentes para cada producto y, por último, es posible variar el producto que se quiere crear no sólo únicamente por su subclase (su familia) sino también por valores diferentes de ciertos atributos. Profundizaremos este último...
Reflective Visitor
1. Discusión
Hemos presentado en un capítulo anterior el patrón Visitor para poder agregar nuevas funcionalidades a un conjunto de clases sin tener que modificar estas clases tras cada agregación. Cada nueva funcionalidad da pie a una clase de visitante que implementa esta funcionalidad incluyendo un conjunto de métodos, uno por cada clase. Todos estos métodos tienen el mismo nombre, por ejemplo visita, y tienen un único parámetro cuyo tipo es el de la clase para la que se implementa la funcionalidad.
No obstante para implementar el patrón Visitor, las clases que deben ser visibles requieren una ligera modificación, a saber la inclusión de un método para aceptar al visitante, método cuyo único fin es invocar al método visita con un parámetro correctamente tipado. El nombre de este método es a menudo acepta o aceptaVisitante.
La figura 5-1.5 muestra una implementación del patrón Visitor con el objetivo de visitar una jerarquía de objetos descrita mediante el patrón Composite. Estos objetos son empresas que en ocasiones, cuando se trata de las empresas madres, poseen filiales. Las dos funcionalidades agregadas son el cálculo de los costes de mantenimiento y la posibilidad de enviar un mailing comercial a una empresa y a todas sus filiales, incluyendo a las filiales de las filiales. El método aceptaVisitante de la clase EmpresaMadre incluye un bucle foreach que solicita a cada una de las filiales que acepte a su visitante.
Figura 5-1.5 - Aplicación del patrón Visitor a un conjunto de empresas
El patrón Reflective Visitor es una variante del patrón Visitor que utiliza la capacidad de reflexión estructural del lenguaje de programación de modo que la implementación no requiera la inclusión del método de aceptación del visitante en las clases que deban ser visitadas. La reflexión estructural de un lenguaje de programación es su capacidad para proveer un medio de examinar el conjunto de clases que forman el programa y su contenido (atributos y métodos). La reflexión estructural existe a día de hoy en la mayor parte de lenguajes de programación orientada a objetos (Java y C# integran esta capacidad).
De este modo la implementación anterior se simplifica con el patrón...
El patrón Multicast
1. Descripción y ejemplo
El objetivo del patrón Multicast es gestionar los eventos producidos en un programa para transmitirlos a un conjunto de receptores afectados. El patrón está basado en un mecanismo de suscripción de receptores en los emisores.
Queremos implementar un programa de envío de mensajes entre las direcciones (general, comercial, financiera, etc.) de un concesionario y sus empleados.
Cada empleado puede suscribirse a la dirección a la que pertenece y recibir todos los mensajes emitidos por ella. Un empleado no puede suscribirse a una dirección a la que no pertenece. Todos los empleados pueden suscribirse a la dirección general para recibir sus mensajes.
La estructura de los mensajes puede variar de una dirección a otra: desde una simple línea de texto para los mensajes comerciales, hasta un conjunto de líneas para los mensajes generales provenientes de la dirección general.
El diagrama de clases de la figura 5-1.8 expone la solución proporcionada por el patrón Multicast. La genericidad de tipos se utiliza para crear un mensaje, un emisor y un receptor abstractos y genéricos, a saber las clases MensajeAbstracto y EmisorAbstracto así como la interfaz ReceptorAbstracto.
Figura 5-1.8 - El patrón Multicast aplicado a un sistema de mensajes
La clase EmisorAbtracto presenta dos funcionalidades:
-
Gestiona un registro (una lista) de receptores con la posibilidad de suscribirse gracias al método agrega.
-
Permite enviar un mensaje al conjunto de receptores presentes en el registro gracias al método envíoMúltiple.
Está basada en dos tipos genéricos, a saber TMensaje que está acotado por MensajeAbstracto y TReceptor acotado por ReceptorAbstracto<TMensaje>. De este modo, cualquier mensaje es obligatoriamente una subclase de MensajeAbstracto y todo receptor una subclase de ReceptorAbstracto<TMensaje>.
La clase MensajeAbstracto es una clase abstracta totalmente vacía. Sólo existe con fines de tipado.
La interfaz ReceptorAbstracto es una interfaz que incluye la firma del método recibe. Esta interfaz está basada en el tipo genérico TMensaje acotado por MensajeAbstracto.
Nos interesamos ahora en el caso particular de los mensajes que provienen de la dirección general. Para estos mensajes, creamos una subclase...