¡Descarga Java ProgramacionJava ProgramacionJava ProgramacionJava Programacion y más Resúmenes en PDF de Programación Java solo en Docsity!
Patrones de diseño en Java
Los 23 modelos de diseño
Este libro presenta de forma concisa y práctica los 23 modelos de diseño ( Design
Patterns ) fundamentales ilustrándolos mediante ejemplos adaptados y fáciles de
comprender. Cada ejemplo se describe en UML y en Java bajo la forma de un pequeño
programa completo y ejecutable. Para cada patrón el autor detalla su nombre, el
problema correspondiente , la solución propuesta , sus dominios de aplicación y su
estructura genérica.
El libro está dirigido a aquellos diseñadores y desarrolladores que trabajen con
Programación Orientada a Objetos. Para comprenderlo bien, es preferible tener
conocimientos previos acerca de los principales elementos de los diagramas de clases
UML y la última versión del lenguaje Java.
El libro está organizado en tres partes que se corresponden con las tres familias de
patrones de diseño: los patrones de construcción , los patrones de estructuración y
los patrones de comportamiento.
A continuación, se incluye un capítulo que presenta tres variantes de patrones
existentes, mostrando la gran flexibilidad existente a la hora de implementar estos
modelos. También se aborda el patrón compuesto MVC (Model-View-Controller).
Los ejemplos utilizados en estas páginas son el resultado de una aplicación de venta
online de vehículos y pueden descargarse en esta página.
Los capítulos del libro:
Prefacio – Parte 1: Introducción – Introducción a los patrones de diseño – Caso de
estudio: venta online de vehículos – Parte 2: Patrones de construcción – Introducción a
los patrones de construcción – El patrón Abstract Factory – El patrón Builder – El
patrón Factory Method – El patron Prototype – El patrón Singleton – Parte 3: Patrones
de estructuración – Introducción a los patrones de estructuración – El patrón Adapter –
El patrón Bridge – El patrón Composite – El patrón Decorator – El patrón Facade – El
patrón Flyweight – El patrón Proxy – Parte 4: Patrones de comportamiento –
Introducción a los patrones de comportamiento – El patrón Chain of Responsability – El
patrón Command – El patrón Interpreter – El patrón Iterator – El patrón Mediator – El
patrón Memento – El patrón Observer – El patrón State – El patrón Strategy – El patrón
Template Method – El patrón Visitor – Parte 5: Aplicación de los patrones –
Composición y variación de patrones – El patrón Composite MVC – Los patrones en el
diseño de aplicaciones – Ejercicios
Laurent DEBRAUWER
Laurent Debrauwer es doctor en informática por la Universidad de Lille 1. Autor de
programas en el dominio de la lingüística y de la semántica, editados por las empresas
META-AGENT Software y Semantica, que él mismo dirige. Especialista en el enfoque
mostrando para ello cómo construir nuevos patrones a partir de la composición o la
variación de patrones existentes.
Se dedica otro capítulo adicional al estudio del patrón composite MVC (Model View
Controller) ilustrado mediante una aplicación web enriquecida.
Por último, el anexo está dedicado a pequeños ejercicios de modelización y de
programación basados en el uso de los patrones.
Introducción a los patrones de diseño
Design patterns o patrones de diseño
Un design pattern o patrón de diseño consiste en un diagrama de objetos que forma una
solución a un problema conocido y frecuente. El diagrama de objetos está constituido
por un conjunto de objetos descritos por clases y las relaciones que enlazan los objetos.
Los patrones responden a problemas de diseño de aplicaciones en el marco de la
programación orientada a objetos. Se trata de soluciones conocidas y probadas cuyo
diseño proviene de la experiencia de los programadores. No existe un aspecto teórico en
los patrones, en particular no existe una formalización (a diferencia de los algoritmos).
Los patrones de diseño están basado en las buenas prácticas de la programación
orientada a objetos. Por ejemplo, la figura 1.1 muestra el patrón Template method que
se describe en el capítulo El patrón Template Method. En este patrón, el método
calculaPrecioConIVA invoca al método calculaIVA abstracto de la clase Pedido. Está
definido en las subclases de Pedido, a saber las clases PedidoEspaña y PedidoFrancia.
En efecto, el IVA varía en función del país. Al método calculaPrecioConIVA se le
llama "modelo" (template method). Introduce un algoritmo basado en un método
abstracto.
Este patrón está basado en el polimorfismo, una propiedad importante de la
programación orientada a objetos. El precio de un pedido en España o en Francia está
sometido a un impuesto sobre el valor añadido. No obstante la tasa no es la misma, y el
cálculo del IVA difiere en España y en Francia. Por consiguiente, el patrón Template
method constituye una buena ilustración del polimorfismo.
Figura 1.1 - Ejemplo de uso del patrón Template Method
Los patrones se introducen en 1995 con el libro del llamado "GoF", de Gang of Four (en
referencia a la "banda de los cuatro" autores), llamado "Design Patterns - Elements of
Reusable Object-Oriented Software" escrito por Erich Gamma, Richard Helm, Ralph
Johnson y John Vlissides. Este libro constituye la obra de referencia acerca de patrones
de diseño.
Descripción de los patrones de diseño
Hemos decidido describir los patrones de diseño con ayuda de los siguientes lenguajes:
El lenguaje de modelización UML introducido por el OMG
(http://www.omg.org).
El lenguaje de programación Java creado por la empresa Oracle
(http://www.java.com).
Los patrones de diseño se describen desde la sección 2 - Patrones de construcción hasta
la sección 4 - Patrones de comportamiento. Para cada patrón se presentan los siguientes
elementos:
El nombre del patrón.
La descripción del patrón.
Un ejemplo describiendo el problema y la solución basada en el patrón descrito
mediante un diagrama de clases UML. En este diagrama, se describe el cuerpo
de los métodos utilizando notas.
La estructura genérica del patrón, a saber:
Su esquema, extraído de cualquier contexto particular, bajo la forma de un
diagrama de clases UML.
Interpreter : proporciona un marco para dar una representación mediante
objetos de la gramática de un lenguaje con el objetivo de evaluar,
interpretándolas, expresiones escritas en este lenguaje.
Iterator : proporciona un acceso secuencial a una colección de objetos sin que
los clientes se preocupen de la implementación de esta colección.
Mediator : construye un objeto cuya vocación es la gestión y el control de las
interacciones en el seno de un conjunto de objetos sin que estos elementos se
conozcan mutuamente.
Memento : salvaguarda y restaura el estado de un objeto.
Observer : construye una dependencia entre un sujeto y sus observadores de
modo que cada modificación del sujeto sea notificada a los observadores para
que puedan actualizar su estado.
State : permite a un objeto adaptar su comportamiento en función de su estado
interno.
Strategy : adapta el comportamiento y los algoritmos de un objeto en función de
una necesidad concreta sin por ello cargar las interacciones con los clientes de
este objeto.
Template Method : permite reportar en las subclases ciertas etapas de una de las
operaciones de un objeto, estando éstas descritas en las subclases.
Visitor : construye una operación a realizar en los elementos de un conjunto de
objetos. Es posible agregar nuevas operaciones sin modificar las clases de estos
objetos.
Cómo escoger y utilizar un patrón de
diseño para resolver un problema
Para saber si existe un patrón de diseño que responde a un problema concreto, la
primera etapa consiste en ver las descripciones de la sección anterior y determinar si
existe uno o varios patrones cuya descripción se acerque a la del problema.
A continuación, conviene estudiar con detalle el o los patrones descubiertos a partir de
su descripción completa que se encuentra en las secciones 2 - Patrones de construcción
a 4 - Patrones de comportamiento. En particular, conviene estudiar a partir del ejemplo
que se proporciona y de la estructura genérica si el patrón responde de forma pertinente
al problema. Este estudio debe incluir principalmente la posibilidad de adaptar la
estructura genérica y, de hecho, averiguar si el patrón una vez adaptado responde al
problema. Esta etapa de adaptación es una etapa importante del uso del patrón para
resolver un problema. La describiremos a continuación.
Una vez escogido el patrón, su uso en una aplicación comprende las siguientes etapas:
Estudiar profundamente su estructura genérica, que sirve como base para utilizar
un patrón.
Renombrar las clases y los métodos introducidos en la estructura genérica. En
efecto, en la estructura genérica de un patrón, el nombre de las clases y de los
métodos es abstracto. Y al revés, una vez integrados en una aplicación, estas
clases y métodos deben nombrarse de acuerdo a los objetos que describen y a las
operaciones que realizan respectivamente. Esta etapa supone el trabajo mínimo
esencial para poder utilizar un patrón.
Adaptar la estructura genérica para responder a las restricciones de la aplicación,
lo cual puede implicar cambios en el diagrama de objetos.
A continuación se muestra un ejemplo de adaptación del patrón Template method a
partir del ejemplo presentado en la primera sección de este capítulo. La estructura
genérica de este patrón se muestra en la figura 1.2.
Figura 1.2 - Estructura genérica del patrón Template Method
Queremos adaptar esta estructura en el marco de una aplicación comercial donde el
método de cálculo del IVA no esté incluido en la clase Pedido sino en las subclases
concretas de la clase abstracta Pais. Estas subclases contienen todos los métodos de
cálculo de los impuestos específicos de cada país.
El método calculaIVA de la clase Pedido invocará a continuación al método calculaIVA
de la subclase del país afectado mediante una instancia de esta subclase. Esta instancia
puede pasarse como parámetro en la creación del pedido.
La figura 1.3 ilustra el patrón adaptado listo para su uso en la aplicación.
Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy,
Template Method y Visitor.
Caso de estudio: venta online de vehículos
Descripción del sistema
En este libro tomaremos un ejemplo de diseño de un sistema para ilustrar el uso de los
veintitrés patrones de diseño.
El sistema que vamos a diseñar es un sitio web de venta online de vehículos como, por
ejemplo, automóviles o motocicletas. Este sistema autoriza distintas operaciones como
la visualización de un catálogo, la recogida de un pedido, la gestión y el seguimiento de
los clientes. Además estará accesible bajo la forma de un servicio web.
Cuaderno de carga
El sitio permite visualizar un catálogo de vehículos puestos a la venta, realizar
búsquedas en el catálogo, realizar el pedido de un vehículo, seleccionar las opciones
para el mismo mediante un sistema de carro de la compra virtual. Las opciones
incompatibles también deben estar gestionadas (por ejemplo "asientos deportivos" y
"asientos en cuero" son opciones incompatibles). También es posible volver a un estado
anterior del carro de la compra.
El sistema debe administrar los pedidos. Debe ser capaz de calcular los impuestos en
función del país de entrega del vehículo. También debe gestionar los pedidos pagados al
contado y aquellos que están ligados a una petición de crédito. Para ello, se tendrá en
cuenta las peticiones de crédito. El sistema administra los estados del pedido: en curso,
validado y entregado.
Al realizar el pedido de un vehículo, el sistema construye el conjunto de documentos
necesarios como la solicitud de matriculación, el certificado de cesión y la orden de
pedido. Estos documentos estarán disponibles en formato PDF o en formato HTML.
El sistema también permite rebajar los vehículos de difícil venta, como por ejemplo
aquellos que se encuentran en stock pasado un tiempo.
También permite realizar una gestión de los clientes, en particular de empresas que
poseen filiales para proporcionarles, por ejemplo, la compra de una flota de vehículos.
Tras la virtualización del catálogo, es posible visualizar animaciones asociadas a un
vehículo. El catálogo puede presentarse con uno o tres vehículos por cada línea de
resultados.
La búsqueda en el catálogo puede realizarse con ayuda de palabras clave y de
operadores lógicos (y, o).
Es posible acceder al sistema mediante una interfaz web clásica o a través de un sistema
de servicios web.
Uso de patrones de diseño
Para cumplir con los distintos requisitos expresados en el cuaderno de carga,
utilizaremos en los siguientes capítulos los patrones de diseño. Se tomarán en cuenta en
las siguientes partes de la concepción del sitio web:
Introducción a los patrones de construcción
Descripción de la sección Patrón de diseño
Construir los objetos de dominio (coche de gasolina,
coche diesel, coche eléctrico, etc.).
Abstract Factory
Construir los conjuntos de documentos necesarios en
caso de comprar un vehículo.
Builder, Prototype
Crear los pedidos. Factory Method
Crear el conjunto en blanco de los documentos. Singleton
Gestionar los documentos PDF. Adapter
Implementar los formularios en HTML o mediante un
applet.
Bridge
Representar las empresas clientes. Composite
Visualizar los vehículos del catálogo.
Decorator, Observer,
Strategy
Proporcionar la interfaz mediante servicios web del sitio. Facade
Administrar las opciones de un vehículo en un pedido. Flyweight, Memento
Administrar la visualización de animaciones para cada
vehículo del catálogo.
Proxy
Administrar la descripción de un vehículo.
Chain of
responsibility
Rebajar los vehículos en stock pasado un periodo
determinado.
Command
Realizar búsquedas en la base de vehículos mediante una
búsqueda escrita en forma de expresión lógica.
Interpreter
Devolver secuencialmente los vehículos del catálogo. Iterator
Gestionar el formulario de una solicitud de crédito. Mediator
Gestionar los estados de un pedido. State
Calcular el importe de un pedido. Template Method
Enviar propuestas comerciales por correo electrónico a
ciertas empresas clientes.
Visitor
// continuación del método }
Este ejemplo muestra que es difícil configurar el mecanismo de creación de objetos, la
clase que se pasa como parámetro al operador new no puede sustituirse por una
variable. El uso de instrucciones condicionales en el código del cliente a menudo resulta
práctico, con el inconveniente de que un cambio en la jerarquía de las clases a instanciar
implica modificaciones en el código de los clientes. En nuestro ejemplo, es necesario
cambiar el código del método construyeDoc si se quiere agregar nuevos tipos de
documento.
En lo sucesivo, ciertos lenguajes ofrecen mecanismos más o menos flexibles y a
menudo bastante complejos para crear instancias a partir del nombre de una clase
contenida en una variable de tipo String.
La dificultad es todavía mayor cuando hay que construir objetos compuestos cuyos
componentes pueden instanciarse mediante clases diferentes. Por ejemplo, un conjunto
de documentos puede estar formado por documentos PDF, RTF o HTML. El cliente
debe conocer todas las clases posibles de los componentes y de las composiciones. Cada
modificación en el conjunto de las clases se vuelve complicada de gestionar.
2. Soluciones propuestas por los patrones de
construcción
Los patrones Abstract Factory, Builder, Factory Method y Prototype proporcionan una
solución para parametrizar la creación de objetos. En el caso de los patrones Abstract
Factory, Builder y Prototype, se utiliza un objeto como parámetro del sistema. Este
objeto se encarga de realizar la instanciación de las clases. De este modo, cualquier
modificación en la jerarquía de las clases sólo implica modificaciones en este objeto.
El patrón Factory Method proporciona una configuración básica sobre las subclases de
la clase cliente. Sus subclases implementan la creación de los objetos. Cualquier cambio
en la jerarquía de las clases implica, por consiguiente, una modificación de la jerarquía
de las subclases de la clase cliente.
El patrón Abstract Factory
Descripción
El objetivo del patrón Abstract Factory es la creación de objetos agrupados en familias
sin tener que conocer las clases concretas destinadas a la creación de estos objetos.
Ejemplo
El sistema de venta de vehículos gestiona vehículos que funcionan con gasolina y
vehículos eléctricos. Esta gestión está delegada en el objeto Catálogo encargado de
crear tales objetos.
Para cada producto, disponemos de una clase abstracta, de una subclase concreta
derivando una versión del producto que funciona con gasolina y de una subclase
concreta derivando una versión del producto que funciona con electricidad. Por
ejemplo, en la figura 4.1, para el objeto Scooter, existe una clase abstracta Scooter y dos
subclases concretas ScooterElectricidad y ScooterGasolina.
El objeto Catálogo puede utilizar estas subclases concretas para instanciar los productos.
No obstante si fuera necesario incluir nuevas clases de familias de vehículos (diésel o
mixto gasolina-eléctrico), las modificaciones a realizar en el objeto Catálogo pueden ser
bastante pesadas.
El patrón Abstract Factory resuelve este problema introduciendo una interfaz
FábricaVehículo que contiene la firma de los métodos para definir cada producto. El
tipo devuelto por estos métodos está constituido por una de las clases abstractas del
producto. De este modo el objeto Catálogo no necesita conocer las subclases concretas y
permanece desacoplado de las familias de producto.
Se incluye una subclase de implementación de FábricaVehículo por cada familia de
producto, a saber las subclases FábricaVehículoElectricidad y
FábricaVehículoGasolina. Dicha subclase implementa las operaciones de creación del
vehículo apropiado para la familia a la que está asociada.
El objeto Catálogo recibe como parámetro una instancia que responde a la interfaz
FábricaVehículo, es decir o bien una instancia de FábricaVehículoElectricidad, o bien
una instancia de FábricaVehículoGasolina. Con dicha instancia, el catálogo puede crear
y manipular los vehículos sin tener que conocer las familias de vehículos y las clases
concretas de instanciación correspondientes.
El conjunto de clases del patrón Abstract Factory para este ejemplo se detalla en la
figura 4.1.
Figura 4.2 - Estructura del patrón Abstract Factory
2. Participantes
Los participantes del patrón son los siguientes:
FábricaAbstracta (FábricaVehículo) es una interfaz que define las firmas de los
métodos que crean los distintos productos.
FábricaConcreta1, FábricaConcreta2 (FábricaVehículoElectricidad,
FábricaVehículoGasolina) son las clases concretas que implementan los
métodos que crean los productos para cada familia de producto. Conociendo la
familia y el producto, son capaces de crear una instancia del producto para esta
familia.
ProductoAbstractoA y ProductoAbstractoB (Scooter y Automóvil) son las clases
abstractas de los productos independientemente de su familia. Las familias se
introducen en las subclases concretas.
Cliente es la clase que utiliza la interfaz de FábricaAbstracta.
3. Colaboraciones
La clase Cliente utiliza una instancia de una de las fábricas concretas para crear sus
productos a partir de la interfaz FábricaAbstracta.
Normalmente sólo es necesario crear una instancia de cada fábrica concreta, que puede
compartirse por varios clientes.
Dominios de uso
El patrón se utiliza en los dominios siguientes:
Un sistema que utiliza productos necesita ser independiente de la forma en que
se crean y agrupan estos productos.
Un sistema está configurado según varias familias de productos que pueden
evolucionar.
Ejemplo en Java
Presentamos a continuación un pequeño ejemplo de uso del patrón escrito en
Java. El código Java correspondiente a la clase abstracta Automovil y sus
subclases aparece a continuación. Es muy sencillo, describe los cuatro atributos
de los automóviles así como el método mostrarCaracteristicas que permite
visualizarlas.
public abstract class Automovil { protected String modelo; protected String color; protected int potencia; protected double espacio; public Automovil(String modelo, String color, int potencia, double espacio) { this.modelo = modelo; this.color = color; this.potencia = potencia; this.espacio = espacio; } public abstract void mostrarCaracteristicas(); }
public abstract void mostrarCaracteristicas();
}
public class ScooterElectricidad extends Scooter
{
public ScooterElectricidad(String modelo, String color,
int potencia)
{
super(modelo, color, potencia);
}
public void mostrarCaracteristicas()
{
System.out.println("Scooter electrica de modelo: " +
modelo + " de color: " + color +
" de potencia: " + potencia);
}
}
public class ScooterGasolina extends Scooter
{
public ScooterGasolina(String modelo, String color,
int potencia)
{
super(modelo, color, potencia);
}
public void mostrarCaracteristicas()
{
System.out.println("Scooter de gasolina de modelo: " +
modelo + " de color: " + color +
" de potencia: " + potencia);
}
}
Ahora podemos introducir la interfaz FabricaVehiculo y sus dos clases de
implementación, una para cada familia (eléctrico/gasolina). Es fácil darse cuenta
de que sólo las clases de implementación utilizan las clases concretas de los
vehículos.
public interface FabricaVehiculo
{
Automovil creaAutomovil(String modelo, String color,
int potencia, double espacio);
Scooter creaScooter(String modelo, String color, int
potencia);
}
public class FabricaVehiculoElectricidad implements FabricaVehiculo
{
public Automovil creaAutomovil(String modelo, String
color, int potencia, double espacio)
{
return new AutomovilElectricidad(modelo, color,
potencia, espacio);
}
public Scooter creaScooter(String modelo, String
color, int potencia)
{
return new ScooterElectricidad(modelo, color,
potencia);
}
}
public class FabricaVehiculoGasolina implements FabricaVehiculo
{
public Automovil creaAutomovil(String modelo, String color, int potencia, double espacio)
{
return new AutomovilGasolina(modelo, color,
potencia, espacio);
}
public Scooter creaScooter(String modelo, String
color, int potencia)
{
return new ScooterGasolina(modelo, color, potencia);
}
}
Por último, se presenta el código fuente Java del cliente de la fábrica, a saber el
catálogo que es, en nuestro ejemplo, el programa principal. Por motivos de
simplicidad, el catálogo solicita al comienzo la fábrica que se quiere utilizar
(electricidad o gasolina). Esta fábrica debería proporcionarse como parámetro al
catálogo.
El resto del programa es totalmente independiente de la familia de objetos,
respetando el objetivo del patrón Abstract Factory.
import java.util.*;
public class Catalogo
{
public static int nAutos = 3;
public static int nScooters = 2;
public static void main(String[] args)
{
Scanner reader = new Scanner(System.in);
FabricaVehiculo fabrica;
Automovil[] autos = new Automovil[nAutos];
Scooter[] scooters = new Scooter[nScooters];
System.out.print("Desea utilizar " +
"vehiculos electricos (1) o a gasolina (2):");
String eleccion = reader.next();