Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
PATRONES DE DISEÑO
OBJETIVO
• Aplicar el polimorfismo a la implementación de algunos patrones de diseño.
1. INTRODUCCIÓN
Según Grady Booch, “Una arquitectura orientada a objetos bien estructurada está llena de patrones.
La calidad de un sistema orientado a objetos se mide por la atención que los diseñadores han prestado
a las colaboraciones entre sus objetos. Los patrones conducen a arquitecturas más pequeñas, más
simples y más comprensibles”.
Diseñar software orientado a objetos que cumpla con el paradigma no es tan simple, pero diseñar
software orientado a objetos reutilizable es más difícil todavía. Diseños generales y flexibles son muy
difíciles de conseguir la primera vez. En este punto, se podría formular la pregunta, ¿Qué
conocimientos posee un programador experto que desconoce uno que se considera novato? La
respuesta ante este interrogante está en la capacidad de reutilizar soluciones que funcionaron en el
pasado, es decir, aprovechar la experiencia y la utilización de patrones. La clave para la reutilización de
software está en anticiparse a los nuevos requisitos y cambios, de modo que los sistemas evolucionen
de forma adecuada.
Los síntomas de una aplicación informática mal construida son la rigidez, la fragilidad y la
inmovilidad. La rigidez hace referencia a la tendencia del software a ser difícil de cambiar. La
fragilidad está relacionada con la rigidez, y se nota cuando un cambio en alguna parte del software,
ocasiona cambios en otros sectores. Finalmente, la inmovilidad es la inhabilidad para reutilizar el
software.
Los patrones describen un problema recurrente y una solución en un contexto dado (Alexander, Sara,
Max, Fiksdahl-King, & Angel, 1977). En esta definición, el término contexto se refiere al conjunto de
condiciones o situaciones en las que un patrón dado es aplicable.
Los patrones de diseño proporcionan soluciones elegantes y reutilizables a los problemas más
frecuentes de desarrollo de software en un contexto particular. Esto significa que un patrón que está
1
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
Existen varios tipos de patrones. Se tiene los patrones de código, que están a nivel de lenguaje de
programación. Se tiene patrones de arquitectura, que son aquellos que expresan un esquema
organizativo estructural fundamental para sistemas de software. Finalmente, están los patrones de
diseño, que son descripciones de clases cuyas instancias colaboran entre sí, que deben ser adaptados
para resolver problemas de diseño general en un particular contexto. En este libro presta atención
particular a los patrones de diseño.
Los patrones de diseño son descripciones de clases y objetos relacionados que están adaptados para
resolver un problema de diseño general en un contexto determinado. Un patrón de diseño identifica
Clases, Roles, Colaboraciones y la distribución de responsabilidades. Cada patrón nombra, explica y
evalúa un diseño recurrente en sistemas orientados a objetos.
Existen diferentes tipos de patrones en el desarrollo de software, dependiendo del nivel de abstracción
(y granularidad), del contexto particular en el cual se aplican o de la etapa en proceso de desarrollo.
Dentro del contexto del diseño de software, los tipos más representativos son:
2
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
(código). Su uso no necesariamente se refleja en la estructura global del sistema. Por ejemplo el
esquema observador-observado, donde se busca especificar el comportamiento de la
comunicación entre dos entidades de tal forma que queden desacopladas totalmente una de otra.
Idioms: son patrones directamente involucrados en la codificación, por tanto son más simples y
específicos al lenguaje, normalmente vienen incluidos como plantillas en los ambientes de
desarrollo. Por ejemplo, como iniciar un hilo (Thread) en Java.
3
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
Resumen
El patrón Factory Method recomienda encapsular la funcionalidad requerida, para seleccionar
e instanciar la clase apropiada de una jerarquía, dentro de un método denominado factory
method. De esta manera, un factory method puede ser definido como un método de una
clase que selecciona una clase apropiada de una jerarquía de clases basado en el contexto
de la aplicación y otros factores influyentes.
Descripción
Problema
En general, todas las subclases en una jerarquía de clases heredan los métodos
implementados de la clase padre. Una subclase puede sobrescribir la implementación de la
clase padre para ofrecer diferentes tipos de funcionalidad para el mismo método. Cuando un
objeto de una aplicación es consciente de la funcionalidad exacta que requiere, puede
directamente instanciar la clase de la jerarquía de clases que le ofrece la funcionalidad
requerida. Sin embargo, a veces, un objeto de una aplicación solo puede saber que necesita
acceder a una clase de la jerarquía de clases, pero no la conoce exactamente, es decir, no
sabe exactamente qué clase de entre el conjunto de subclases debe seleccionar. La
selección de una clase apropiada puede depender de factores tales como:
El estado de la aplicación que se ejecuta.
La configuración de la aplicación.
Solución
En tales casos, un objeto de la aplicación necesita implementar los criterios de selección de
la clase para instanciar una clase apropiada de la jerarquía para acceder a sus servicios. En
este caso, el patrón Factory Method recomienda encapsular la funcionalidad requerida, para
seleccionar e instanciar la clase apropiada, dentro de un método denominado factory
method. De esta manera, un factory method puede ser definido como un método de una
clase que:
Selecciona una clase apropiada de una jerarquía de clases basado en el contexto de
la aplicación y otros factores influyentes.
4
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
Instancia la clase seleccionada y la retorna como una instancia del tipo de la clase
padre.
La encapsulación de la implementación requerida para seleccionar e instanciar una clase
apropiada en un método separado tiene las siguientes ventajas:
Los objetos de la aplicación pueden utilizar el método de fábrica para acceder a una
instancia de la clase apropiada. Se elimina la necesidad que un objeto de la
aplicación, tenga que negociar con la variación de los criterios de selección de la
clase.
Además de los criterios de selección de la clase, el método de fábrica también
implementa un mecanismo especial requerido para instanciar la clase seleccionada.
Esto es aplicable si clases diferentes en la jerarquía necesitan ser instanciadas de
diferentes formas. El método de fábrica oculta esos detalles de los objetos de
aplicación y elimina la necesidad de tratar con esas complejidades.
Debido a que el método de fábrica retorna la instancia de la clase seleccionada como
un objeto de tipo clase padre, un objeto de la aplicación no tiene que ser consciente
de la existencia de la clase en la jerarquía.
Ejemplo
Diseñemos la funcionalidad de registro de mensajes en una aplicación. En general, el
registro de mensajes es una de las tareas más comunes en las aplicaciones. Esto es algo útil
en la depuración y monitoreo de aplicaciones.
Debido a que el registro de mensajes puede ser requerido por diferentes clientes, podría ser
una buena idea mantener la funcionalidad en una clase común para que los objetos cliente
no tengan que repetir esos detalles.
En general, un mensaje entrante podría ser registrado en diferentes medios, en diferentes
formatos. Se puede definir una interfaz Logger (En español Registrador) utilizada por objetos
clientes que registran mensajes, y clases concretas que implementan esta interface. La clase
implementadora FileLogger almacena los mensajes entrantes en un archivo. La clase
implementadora ConsoleLogger visualiza los mensajes entrantes en pantalla. Este diseño se
puede visualizar en la Figura 1.
5
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
6
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
7
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
Implementación en
Java
A continuación la implementación en Java.
package patrones.factorymethod.ejm1;
public interface Logger {
public void log(String msg);
}
package patrones.factorymethod.ejm1;
public class ConsoleLogger implements Logger {
public void log(String msg) {
System.out.println(msg);
}
}
package patrones.factorymethod.ejm1;
public class FileLogger implements Logger {
public void log(String msg) {
FileUtil futil = new FileUtil();
futil.writeToFile("log.txt", msg, true, true);
}
}
package patrones.factorymethod.ejm1;
import java.io.IOException;
import java.util.Properties;
public class LoggerFactory {
public boolean isFileLoggingEnabled() {
Properties p = new Properties();
try {
p.load(ClassLoader.getSystemResourceAsStream("logger.properties"));
String fileLoggingValue = p.getProperty("FileLogging");
if (fileLoggingValue.equalsIgnoreCase("ON") == true)
return true;
else
return false;
} catch (IOException e) {
return false;
}
}
public Logger getLogger() {
if (isFileLoggingEnabled()) {
8
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
package patrones.factorymethod.ejm1;
public class LoggerTest {
public static void main(String[] args) {
LoggerFactory factory = new LoggerFactory();
Logger logger = factory.getLogger();
logger.log("A Message to Log");
}
}
FileLogging=ON
La Figura 4 muestra los archivos fuente de este ejemplo en el entorno eclipse IDE:
9
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
Ejercicios
1. Adicione al proyecto anterior un nuevo logger DBLogger que registre mensajes en una
base de datos.
Resumen
El patrón Adapter (Adaptador) se utiliza para transformar una interfaz en otra, de tal modo que una
clase que no pudiera utilizar la primera, haga uso de ella a través de la segunda. Convierte la interfaz de
una clase en otra interfaz que el cliente espera.
Descripción
Problema
En general, los clientes de una clase acceden a los servicios ofrecidos por la clase a través de su
interfaz. A veces, una clase existente puede proporcionar la funcionalidad requerida por un cliente, pero
su interfaz no puede ser lo que el cliente espera. Esto podría suceder debido a diversas razones, por
ejemplo, la interfaz existente puede ser demasiado detallada, o puede carecer de detalle, o la
terminología utilizada por la interfaz puede ser diferente de lo que el cliente está buscando.
En tales casos, la interfaz existente tiene que ser convertida en otra interfaz, lo que el cliente espera,
preservando la reutilización de la clase existente. Sin dicha conversión, el cliente no será capaz de
utilizar la funcionalidad ofrecida por la clase. Esto se puede lograr utilizando el modelo de adaptador.
Solución
El patrón Adaptador sugiere definir una clase alrededor del objeto incompatible con la interfaz. Este
objeto contenedor se conoce como adaptador y el objeto que envuelve se refiere como un adaptado. El
adaptador proporciona la interfaz requerida esperada por el cliente. La implementación de la interfaz
adaptador convierte las solicitudes de cliente en llamadas a la interfaz de la clase adapte. En otras
palabras, cuando un cliente llama a un método adaptador, internamente la clase del adaptador llama a
un método de la clase adaptada, que el cliente no tiene conocimiento. Esto le da al cliente el acceso
indirecto a la clase adaptada. Por lo tanto, un adaptador puede ser utilizado para hacer que las clases
trabajen juntas, lo cual no podría ser de otra manera debido a las interfaces incompatibles.
En resumen, el patrón Adapter (Adaptador) se utiliza para transformar una interfaz en otra, de tal
modo que una clase que no pudiera utilizar la primera, haga uso de ella a través de la segunda.
Convierte la interfaz de una clase en otra interfaz que el cliente espera. Adapter permite a las clases
10
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
trabajar juntas, lo que de otra manera no podría hacerlo debido a sus interfaces incompatibles. También
es conocido como Wrapper (Envoltorio).
La término interfaz usado en la discusión anterior:
No se refiere al concepto de una interfaz en lenguaje de programación Java o C#, aunque la
interfaz de una clase puede ser declarada mediante una interfaz Java o C#.
No se refiere a la interfaz de usuario de una aplicación típica de interfaz gráfica de usuario que
consiste de los controles de las ventanas y GUI.
Se refiere a la interfaz de programación que una clase expone, que es destinado a ser utilizado
por otras clases. Como ejemplo, cuando una clase está diseñada como una clase abstracta o una
interfaz Java, el conjunto de métodos declarado en él constituye la interfaz de la clase.
Adaptadoras de clases Vs. Adaptadores de objetos
Los adaptadores se pueden clasificar en dos categorías: adaptadores de Clases y adaptadores de objetos
(basados en la forma en que un adaptador es diseñado).
Adaptador de clase. Un adaptador de clase está diseñado por una subclase de la clase adaptada.
Además, un adaptador de clase implementa la interfaz esperada por el objeto cliente. Cuando un objeto
cliente invoca un método de clase adaptador, el adaptador llama internamente a un método de la clase
adaptada.
Adaptador de objeto. Un adaptador de objeto contiene una referencia a un objeto del adaptado.
Similar a una adaptador de clase, un adaptador de objeto también implementa la interfaz, de lo que el
cliente espera. Cuando un objeto de cliente llama a un método de objeto adaptador, el adaptador de
objeto invoca un método apropiado sobre la instancia adaptada, quien referencia su contenido. La
Tabla 1 enumera en detalles las diferencias entre adaptadores de clases y adaptadores de objetos.
11
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
12
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
2. Implementación en
Java
package patrones.adapter.eje1;
public abstract class Motor {
abstract public void encender();
package patrones.adapter.eje1;
public class MotorGaston extends Motor {
public MotorGaston() {
super();
System.out.println("Creando el motor gaston");
}
13
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
public MotorEconomico() {
super();
System.out.println("Craendo motor economico");
}
package patrones.adapter.eje1;
/**
*
* La empresa que construye motores electricos nos manda su propia
* implementacion de su nuevo MotorElectrico:
*
*/
public class MotorElectrico {
public MotorElectrico() {
System.out.println("Creando motor electrico");
this.conectado = false;
}
14
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
package patrones.adapter.eje1;
/**
* El adapter de objeto, se encarga no solo de corregir los nombres de los metodos,
sino
* tambien cosas como conectar y desconectar el motor, cosas que a nuestra
* implementacien no le importan. Pero lo mas importante es que ahora podemos
* utilizar esta implemetanacion de Motor en nuestro sistema.
*
*/
public class MotorElectricoAdapter extends Motor {
private MotorElectrico motorElectrico;
public MotorElectricoAdapter() {
super();
this.motorElectrico = new MotorElectrico();
15
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
package patrones.adapter.eje1;
public class ClienteMain {
public static void main(String [] args){
Motor motor = new MotorEconomico();
motor.encender();
motor.acelerar();
motor.apagar();
System.out.println();
motor = new MotorGaston();
motor.encender();
motor.acelerar();
motor.apagar();
System.out.println();
motor = new MotorElectricoAdapter() ;
motor.encender();
motor.acelerar();
motor.apagar();
}
}
Ejercicios
1. Durante el estudio del patrón Factory Method, se diseñó un sistema de registro de mensajes.
Se tenía la clase FileLogger con el método Log, el cual registra un mensaje de un cliente.
16
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
Asumamos que un cliente LoggerClient espera una clase de registro de mensajes que
proporciona una interfaz como la siguiente:
17
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
Resumen
El patrón Observer es útil para diseñar un modelo de comunicación consistente entre un conjunto de
objetos dependientes y un objeto del que dichos objetos dependen. Esto permite que los objetos
dependientes tengan su estado, sincronizado con el objeto del cual dependen. El conjunto de objetos
dependientes se les llama observadores (observers) y el objeto del que ellos dependen se le llama sujeto
(subject).
Descripción
Problema
En ocasiones se requiere diseñar un modelo de comunicación consistente entre un conjunto de objetos
dependientes y un objeto del que dichos objetos dependen. Esto permite que los objetos dependientes
tengan su estado, sincronizado con el objeto del cual dependen. El conjunto de objetos dependientes se
les llama observadores (observers) y el objeto del que ellos dependen se le llama sujeto (subject).
Un observador típico es un objeto con interés o dependencia en el estado del sujeto. Un sujeto puede
tener más de un observador. Cada uno de los observadores necesita conocer cuándo el sujeto sufre un
cambio de estado.
Solución
El sujeto no puede mantener una lista estática de observadores. Por lo tanto, cualquier objeto con
interés en el estado del sujeto necesita explícitamente registrarse como un observador del sujeto. En
todo momento, cuando el sujeto sufre un cambio de estado, notifica a todos sus observadores
registrados. Después de recibir la notificación del sujeto, cada observador consulta el sujeto para
sincronizar su estado. De esta manera el sujeto se comporta como un publicador, publicando mensajes
con todos sus observadores suscritos.
El sujeto debe suministrar una interface para registrar y des-registrar los cambios y notificaciones.
Una de las dos consignas debe ser verdadera:
1. En el modelo, el sujeto debe suministrar una interface que active los observadores para
consultar el sujeto información del estado para poder actualizar su estado.
2. En el modelo, el sujeto debe enviar la información de estado que los observadores pueden estar
interesados.
Los observadores deben suministrar una interface para recibir notificaciones del sujeto.
18
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
La Figura 6, muestra la estructura de la lista de requisitos del patrón observer. De este diagrama se debe
tener en cuenta:
Todos los sujetos esperan proveer la implementación de una interfaz similar a la interfaz
Observable.
Todos los observadores esperan esperan tener una interfaz similar a la interfaz Observer.
Bajo este esquema diferentes observadores pueden ser adicionados dinámicamente.
Ejemplo
A continuación se presenta un ejemplo muy sencillo que ilustra la aplicación del patrón observer. El
Sujeto es un contador. El contador mantiene un valor entero en el rango desde 0 hasta máximo, donde
máximo se establece en el constructor del objeto. La clase dispone de métodos para modificar el valor y
para acceder al estado (es estado lo conforman los valores de los atributos: valor y máximo).
A continuación se muestra la Tabla 2, que resume las clases que intervienen en la aplicación, su rol y la
funcionalidad, acorde a la Figura 6.
Observable Observable Clase abstracta que representa los objetos susceptibles de ser
observados. Incorpora métodos para agregar y eliminar observadores y un
método de notificación. La asociación Observable-Observadores se
implementa mediante un vector de observadores.
Observador Observador Los observadores deben implementar la interfaz Observador, es decir,
tienen un método actualizar() para reaccionar a cambios del sujeto.
Observador ConcreteOb Observador muy simple que ni siquiera consulta el estado del sujeto.
19
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
Concreto1 server
Observador ConcreteOb Un ejemplo de observador concreto de la clase contador (sujeto)
Concreto2 server
Observador ConcreteOb Un ejemplo de observador concreto de la clase contador (sujeto)
Concreto3 server
ClienteMain Cliente Un Cliente sencillo que prueba la ejecución del patrón
Tabla 2. Clases del ejemplo - Patrón Observer
Implementación en
Java
A continuación el código fuente en java.
/**
* Ejemplo de sujeto observable o sujeto concreto. Es una clase que mantiene un
valor entero en
* el rango 0..maximo, donde maximo se establece en la construccion. La clase
* dispone de metodos para modificar el valor y para acceder al estado
* (valor, maximo). Extiende la clase observable heredando su
*/
/*
* El constructor inicializa el estado del objeto: el maximo y elvalor
* inicial, siempre en el rango 0..maximo. Adicionalmente,inicializa la
* parte de Observable que tiene un Contador...
*/
public Contador(int valor, int maximo) {
20
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
super();
this.maximo = maximo;
this.valor = enRango(valor);
}
/**
* Estos dos metodos permiten el acceso al estado del contador
* @return el valor del contador
*/
public int getValor() {
return valor;
}
/*
* Este metodo afecta al estado: primero se modifica de forma consistenteel
* estado y despues se notifica a los observadores del cambio
*/
public void incrementarContador(int n) {
valor = enRango(valor + n);
notificarObservadores();
}
}
/**
* Esta clase abstracta representa los objetos susceptibles de ser
* observados. Incorpora metodos para agregar y eliminar observadores
* y un metodo de notificacion. La asociacion Observable-Observadores
* se implementa mediante un vector de observadores
*/
import java.util.ArrayList;
21
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
/**
* Los observadores deben implementar la siguiente interfaz, es decir,
* tienen un metodo actualizar() para reaccionar a cambios del sujeto
*/
public interface Observador {
public void actualizar();
}
/**
* Observador muy simple que ni siquiera consulta el estado del sujeto.
*
*/
/**
* Un ejemplo de observador concreto de la clase contador.
*
*/
22
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
/*
* Tras ser notificado de un cambio, un observador Medidor accedeal estado
* del Contador utilizando la asociacion
*/
/**
* Un ejemplo de observador concreto de la clase contador.
*
*/
/*
* Tras ser notificado de un cambio, un observador ValorContador accedeal
* estado del Contador utilizando la asociacion
*/
public void actualizar() {
System.out.println("Valor del contador de ObservadorConcreto3 es "
+ contador.getValor());
}
}
/**
* Cliente
*
*/
public class ClienteMain {
public static void main(String... argv) {
c.agregarObservador(oc1);
23
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
c.agregarObservador(oc2);
System.out.println("Primer Incremento");
c.incrementarContador(5);
System.out.println("\nSegundo Incremento");
c.incrementarContador(5);
c.eliminarObservador(oc1);
System.out.println("\nTercer Incremento");
c.incrementarContador(5);
Primer Incremento
Observador concreto simple recibe actualizar!
Valor del contador de ObservadorConcreto1: 105
Valor del contador de ObservadorConcreto3 es 105
Segundo Incremento
Observador concreto simple recibe actualizar!
Valor del contador de ObservadorConcreto1: 110
Valor del contador de ObservadorConcreto3 es 110
Tercer Incremento
Valor del contador de ObservadorConcreto1: 115
Valor del contador de ObservadorConcreto3 es 115
Ejercicios
1. Diseñe e implemente una aplicación para el monitoreo y reporte de diferentes eventos, con las
siguientes funcionalidades (utilizar el patrón observer):
Cuando un evento ocurra, se envía el evento a un objeto EventManager el cual funciona como
un sujeto.
Cuando EventManager recibe un evento, lo almacena en una base de datos y envía
notificaciones a tres objetos, los cuales toman una acción necesaria:
o Un objecto AlertSender envía notificaciones por email a diferentes usuarios donde se
notifica que el evento ha ocurrido.
o Dos objetos de reportes visualizan el evento en diferentes formatos.
2. Construya una aplicación de reporte de ventas para la gestión de un almacén con múltiples
departamentos. Las características de la aplicación incluye:
Los usuarios deben poder seleccionar un departamento. Después de seleccionar el
departamento, dos tipos de reportes se visualizan (utilizar el patrón observer):
24
Universidad del Cauca
Facultad de Ingeniería Electrónica y Telecomunicaciones
o Reporte mensual: que consiste en una lista de transacciones del mes actual para el
departamento seleccionado.
o Gráfico de ventas del año (YTD - Year-to-Date sales): que consiste en un gráfico de
barras que muestra las ventas del año del departamento seleccionado por meses.
En cualquier momento, cuando un departamento es seleccionado, los dos reportes deben ser
refrescados con los datos correspondientes a ese departamento.
25