Programación Avanzada
Introducción Tratan los problemas del diseño que se
repiten y que se presentan en situaciones particulares del diseño, con el fin de proponer soluciones a ellas.
Soluciones exitosas a problemas comunes. Descripción a un problema que ocurre una
y otra vez en nuestro entorno, así como la solución a ese problema, de tal modo que se pueda aplicar esta solución un millón de veces, sin hacer lo mismo dos veces.
Antecedentes
The timeless way of building
Using Pattern Languages for OO Programs.
Libro Design Patterns
Relación entre Patrónes
Patrón Singleton
Patrón Singleton
Aplicabilidad
Instancia de una clase y accesible Instancia extensible: Herencia
Patrón Singleton
Consecuencias
Única instancia controla el acceso. Refinamiento de operaciones y
representación. Permite mas de una instancia. Clientes no se preocupan si existe una
instancia.
Patrón Singleton
Implementación
Método estático y privado. Método estático y publico (instancia) Constructor privado o protegido.
Patrón Singleton
Ejemplo
Patrón Singletonpublic class Singleton { private static Singleton INSTANCE = null; // constructor privado private Singleton() {} // creador sincronizado para protegerse de posibles //problemas multi-hilo private synchronized static void createInstance() { if (INSTANCE == null) { INSTANCE = new Singleton(); } }
public static Singleton getInstance() { if (INSTANCE == null) createInstance(); return INSTANCE; } }
Patrón Abstract Fabric
Provee una interfaz para crear familias de objetos relacionados o dependientes, sin especificar sus clases concretas.
Patrón Abstract Factory
Patrón Abstract Factory El objeto "fábrica" tiene la responsabilidad de
proveer la funcionalidad y el protocolo para la creación de la familia completa.
Los clientes nunca deben crear objetos directamente.
Es fácil cambiar las familias de productos que se utilizan, porque el tipo específico de cada instancia aparece sólo una vez en la aplicación: en el método de creación donde se crea la instancia.
Patrón Abstract FactoryAplicabilidad
Un sistema deba ser independiente de cómo sus productos son creados, representados o compuestos
Una clase no pueda anticipar la clase de objetos que debe crear
Un sistema deba usar sólo una de un conjunto de familias de productos
Una familia de objetos de productos relacionados es creada para ser utilizada en conjunto.
Patrón Abstract Factory
Patrón Abstract FactoryEjemplo
Patrón Abstract Factory
Ejemplo2
Una aplicación para simulación de animales de diferentes zonas geográficas requiere representar animales herbívoros y carnívoros, del continente africano y del continente americano.
Rich Text Format
Solución en código java
Patrón Prototype
Patrón Prototype// Los productos deben implementar esta interfacepublic
interface Producto extends Cloneable { Object clone(); // Aqui van todas las operaciones comunes a los
productos que genera la factoria}
// Un ejemplo basico de productopublic class UnProducto implements Producto {
private int atributo;
UnProducto(int atributo) { this.atributo = atributo; } public Object clone() { return new UnProducto(this.atributo); }
public String toString() { return ((Integer)atributo).toString(); }}
// La clase encargada de generar objetos a partir de los prototipos
public class FactoriaPrototipo { private HashMap mapaObjetos; private String nombrePorDefecto;
public FactoriaPrototipo() { mapaObjetos = new HashMap(); // Se incluyen al mapa todos los productos prototipo
mapaObjetos.put("producto 1", new UnProducto(1));
}
public Object create() { return create(nombrePorDefecto); }
public Object create(String nombre) { nombrePorDefecto = nombre; UnProducto objeto = (UnProducto)mapaObjetos.get(nombre); return objeto != null ? objeto.clone() : null; }
}
public class PruebaFactoria { static public void main(String[] args)
{ FactoriaPrototipo factoria = new FactoriaPrototipo();
Producto producto = (Producto) factoria.create("producto 1"); System.out.println ("Este es el objeto creado: " + producto);
}}
Patrones Estructurales
Patrón Adapter
Convierte la interfaz de una clase en una interfaz que el cliente espera.
Permite que clases inicialmente incompatibles interactúen.
Patrón Adapter El cliente
("Client"), se comunica con la clase adaptadora que actúa de intermediaria, en lugar de con la clase que realmente proporciona la funcionalidad ("Adaptee").
Participantes
Client
Adaptee
Adapter
Patrón AdapterColaboraciones Client llama a las operaciones sobre una
instancia Adapter. De hecho, el adaptador llama a las operaciones de Adaptee que llevan a cabo el pedido.
La clase adaptadora se interpone entre el cliente y la
clase reutilizada. La clase adaptadora presenta la
interfaz que espera la aplicación, y se comunica con
la clase reutilizada, "traduciendo" los mensajes.
Patrón BridgeAplicabilidad Se puede usar el patrón Bridge cuando: Se desea evitar un enlace permanente entre la
abstracción y su implementación. Esto puede ser debido a que la implementación debe ser seleccionada o cambiada en tiempo de ejecución.
Tanto las abstracciones y sus implementaciones deben ser extensibles por medio de subclases. En este caso, el patrón Bridge permite combinar abstracciones e implementciones diferentes y extenderlas independientemente.
Patrón Bridge El puente permite separar dos aspectos
distintos de una jerarquía de clases. . Básicamente, mientras una jerarquía
implementa la funcionalidad real, la(s) otra(s)
representan diversos aspectos, como representación en
diferentes medios
Participantes
Abstraction
RefinedAbstraction
Abstraction
RefinedAbstraction
ImplementorImplementor
ConcreteImplementor ConcreteImplementor
Patrón Bridge
Patrón BridgeEstructura
Patrón Decorador Componente
Define la interfaz para objetos a los que se puede añadir responsabilidades dinámicamente
Componente ConcretoDefine un objeto al que se le pueden agregar
responsabilidades adicionales Decorador
Mantiene una referencia a un objeto Componente y define una interfaz que se ajusta a la interfaz del Componente
Decorador ConcretoAñade responsabilidades al componente
Patrón Decorador Ventajas
Más flexibilidad que la herencia estática. Brinda una manera más flexible de añadir responsabilidad que la que podría obtenerse mediante herencia. Las responsabilidades añadidas se pueden quitar.
Evita clases cargadas de funciones en la parte superior de la jerarquía○ Pagar sólo por lo que se necesita, añadiendo
responsabilidades mediante objetos decoradores
Patrón Decorador Inconvenientes
Un decorador y su componente no son idénticos: un decorador es como un revestimiento de un objeto pero no es idéntico al componente en sí.
Muchos objetos pequeños: el patrón decorador genera muchos objetos pequeños muy parecidos, sólo se diferencian por la forma en que están interconectados
Patrón DecoradorAplicabilidad
El decorador se debe usar cuando: Se requiera adicionar responsabilidades a
objetos individuales dinámicamente sin afectar otros objetos.
Se necesite agregar responsabilidades que pueden ser retiradas
No es práctico adicionar responsabilidades por medio de la herencia.
Patrón DecoradorEjemplo
Se tiene la siguiente jerarquía de clases para una cafetería que vende varios tipos de café.
Solución Sin Patrón Decorador
Patrón DecoradorSolución del Ejemplo
Iniciar con un objeto Bebida y “decorarlo” en tiempo de ejecución con los sabores y agregados deseados.
Por ejemplo:1. Tomar un objeto TuesteOscuro
2. Decorarlo con un objeto Mocca
3. Decorarlo con un objeto CremaBatida
4. Llamar al método costo() y descansar en la delegación para añadir los costos de cada condimento (mocca y crema batida)
Solución
costo()TuesteOscuro
costo() Mocca
costo()TuesteOscuro
costo() CremaBatida
costo() Mocca
costo()TuesteOscuro
1
2
3
Luego se calcula el costo:
CremaBatida llama a costo() en Mocca, éste a su vez llama a costo() en TuesteOscuro
TuesteOscuro retorna su costo y así sucesivamente hasta llegar a CremaBatida donde se obtiene el costo total final
Sobre los decoradores: Tienen el mismo supertipo que los
objetos que decoran Un objeto puede ser decorado con N
decoradores El decorador agrega su propio
comportamiento antes o después de delegar al objeto que decora el resto del trabajo
Patrón DecoradorEstructura
public abstract class Bebida{
String descripcion = "Bebida desconocida";
public String getDescripcion(){
return descripcion;}
public abstract double costo();}
public abstract class DecoradorCondimento extends Bebida{
public abstract String getDescripcion();}
public class Espresso extends Bebida{
public Espresso(){ descripcion = "Espresso"; }
public double costo(){ return 1.99; }
}
public class CombinacionDeLaCasa extends Bebida{
public CombinacionDeLaCasa(){ descripcion="Combinacion de la casa"; }
public double costo(){ return 0.89 }
}
public class TuesteOscuro extends Bebida{
public TuesteOscuro(){ descripcion="Tueste Oscuro"; }
public double costo(){ return 0.99; }
}
public class Descafeinado extends Bebida{
public Descafeinado(){ descripcion="Sin cafeina"; }
public double costo(){ return 1.05; }
}
public class Mocca extends DecoradorCondimento{
Bebida bebida;
public Mocca(Bebida bebida){
this.bebida = bebida;}
public String getDescripcion(){ return bebida.getDescripcion() + ", Mocca";}
public double costo(){
return 0.2 + bebida.costo();} }
public class CremaBatida extends DecoradorCondimento{
Bebida bebida;
public CremaBatida(Bebida bebida){
this.bebida = bebida;}
public String getDescripcion(){
return bebida.getDescripcion() + ", Crema Batida";}
public double costo(){
return 0.1 + bebida.costo();}
}
public class Amaretto extends DecoradorCondimento{
Bebida bebida;
public Amaretto(Bebida bebida){
this.bebida = bebida;}
public String getDescripcion(){
return bebida.getDescripcion() + ", amaretto";}
public double costo(){
return 0.2 + bebida.costo();}
}
public class StarbuzzCafe{
public static void main(String args[]){
Bebida bebida = new Espresso();
System.out.println(bebida.getDescripcion() + " " + bebida.costo());
Bebida segundaTaza = new TuesteOscuro();
segundaTaza = new Mocca(segundaTaza);segundaTaza = new Mocca(segundaTaza);segundaTaza = new CremaBatida(segundaTaza);
System.out.println(segundaTaza.getDescripcion() + " " + segundaTaza.costo());
Bebida tercera = new CombinacionDeLaCasa();
tercera = new Mocca(tercera);tercera = new CremaBatida(tercera);tercera = new Amaretto(tercera);
System.out.println(tercera.getDescripcion() + " " + tercera.costo());
}}
Patrón Façade Estructura
Fuente: dofactory
Patrón FaçadeAplicabilidad
Se utilizará Façade cuando: Se quiera proporcionar una interfaz
simple para un subsistema complejo Haya muchas dependencias entre los
clientes y las clases que implementan una abstracción.
Se quiera dividir en capas los subsistemas
Patrón Façade Consecuencias
VentajasOculta a los clientes los componentes del subsistema
hace el subsistema más fácil de usar y reduce la cantidad de objetos con las que los clientes deben interactuar
Promueve un débil acoplamiento entre el subsistema y sus clientes.
No impide que las aplicaciones cliente usen las clases del subsistema en caso de que sea necesario
Patrón Façade Ejemplo
El siguiente ejemplo muestra el patrón Fachada como un objeto AplicacionPréstamo, el cual provee una interfaz simplificada a un gran subsistema de clases que ayudan a medir y determinar la viabilidad de otorgarle un crédito a un solicitante.
public class Cliente {
private String nombre;
// Constructor public Cliente(String nombre) { this.nombre = nombre; }
public String getNombre() { return nombre; } }
//SUBSISTEMA-CLASE A
public class Banco {
public boolean tieneSuficientesAhorros(Cliente c, int cantidad) { System.out.println("Revisar cuenta bancaria para " + c.getNombre()); return true; } }
//SUBSISTEMA- CLASE Bpublic class Prestamo {
public boolean tienePrestamosPendientes(Cliente c) { System.out.println("Revisar prestamos para " + c.getNombre()); return true; } }
// SUBSISTEMA- CLASE C
public class Credito {
public boolean tieneBuenHistorialCrediticio(Cliente c) { System.out.println("Revisar historial crediticio para " + c.getNombre()); return true;} }
//FACHADA PARA LOS SUBSISTEMAS
public class Hipoteca { private Banco banco = new Banco(); private Prestamo prestamo = new Prestamo(); private Credito credito = new Credito();
public boolean esElegible(Cliente clie, int cantidad) { System.out.println(" aplica para prestamo" + clie.getNombre() + " de " +
cantidad); boolean elegible = true;
// revisa ahorros del aplicante if (!banco.tieneSuficientesAhorros(clie, cantidad)) { elegible = false; } else if (!prestamo.tienePrestamosPendientes(clie)) { elegible = false; } else if (!credito.tieneBuenHistorialCrediticio(clie)) { elegible = false; }
return elegible; } }
public class DemoFacade {
public static void main(String args[]) { // Facade Hipoteca hipoteca = new Hipoteca();
// Evalua la eligibilidad del cliente Cliente c = new Cliente("Ana Mora"); boolean elegible = hipoteca.esElegible(c,125000); String mensaje = elegible? " aprobado(a) " : " rechazado(a)"; System.out.println(c.getNombre() + mensaje);
} }
Patrón Interpreter Es usualmente descrito en términos de
interpretar la gramática del programa. Útil para agregarle funcionalidad estructuras
del patrón Composite. Requiere un método abstracto al nivel más
alto de la estructura compuesta abstracta que es usada por el cliente de los componentes para procesar la estructura completa.
Los métodos concretos son implementados en los componentes, tanto de la terminal y de la no_terminal para hacer el procesamiento actual.
Patrón Interpreter
Patrón Interpreter
Ejemplo: Supongamos que tenemos la siguiente
estructura de árbol binario que contiene valores enteros.
AIntTree representa la un árbol abstracto. Leaf es un nodo hoja que contiene un entero. InternalNode es un nodo interno con dos ramas, las cuales son AIntTree.
Patrón Interpreter Solución:
Para agregarle la posibilidad de sumar los contenidos de todo el árbol, un método abstracto sum() es agregado a la superclase AIntTree. Un cliente que contiene la referencia a un objeto AIntTree va a llamar a sum() en ese objeto. Los métodos sum() concretos son agregados a las subclases Leaf e InternalNode. Leaf.sum() simplemente necesita hacer un return al valor de data. InternalNode.sum() simplemente retorna la suma de left y right.
Patrón Método Plantilla
También conocido como Template Method.
Define en una operación el esqueleto de un algoritmo, delegando en las subclases algunos de sus pasos.
Permite que las subclases redefinan ciertos pasos de un algoritmo sin cambiar su estructura.
Patrón Template Method Se utiliza para implementar las partes de
un algoritmo que no cambian y que sean las subclases quienes implementen el comportamiento que puede variar y para cuando el comportamiento repetido de varias subclases debería factorizarse y ser ubicado en una clase común evitando duplicidad de código.
Se puede definir un método plantilla que llame a operaciones que proporcionan el comportamiento predeterminado que puede ser modificado por las subclases.
Template Method
Tiene la siguiente estructura:
Template Method
Ejemplo:public abstract class Triangle{ Point p1, p2, p3; public Triangle(Point a, Point b, Point c) { p1 = a; p2 = b; p3 = c;} public void draw(Graphics g) { //este método dibuja un triángulo normal drawLine(g, p1, p2); Point current = draw2ndLine(g, p2, p3); closeTriangle(g, current); } public void drawLine(Graphics g, Point a, Point b) {g.drawLine(a.x, a.y, b.x, b.y); } abstract public Point draw2ndLine(Graphics g, Point a, Point b); public void closeTriangle(Graphics g, Point c) { g.drawLine(c.x, c.y, p1.x, p1.y);}}
Patrón Command
Tiene la intención de Tener una interfaz abstracta de manejo de operaciones sobre cierto receptor, permitiendo a un cliente desarrollar las operaciones sin tener que conocer exactamente el tipo y realizar tareas de undo.
Por lo general es utilizado para la asignación de botones y menús.
Patrón Command
Surge porque a veces se quiere poder enviar solicitudes a objetos sin conocer exactamente la operación solicitada ni del receptor de la solicitud. En general un objeto botón o menú ejecuta solicitudes pero la solicitud no está implementada dentro del mismo.
Patrón Command
Patrón Command
Estructura:
Patrón CommandParticipantes
Command
Patrón Command
Ejemplo Patrón Command con Decorador:
Patrón Observer
Define una interfaz para actualizar los objetos que deben ser notificados ante cambios en un sujeto.
Define una dependencia de 1 a muchos, entre objetos, de tal forma que cuando un objeto cambia su estado, todos sus dependientes son notificados y actualizados de forma automática.
Patrón Observer
Patrón ObserverEstructura
Patrón Observer
Sujeto: Proporciona una interfaz para quitar y asignar objetos
Observador: Define una interfaz para actualizar los objetos que deben ser notificados ante cambios en un sujeto
SujetoConcreto: Almacena el estado de interés para los objetos ObservadorConcreto y envía una notificación a sus observadores cuando cambia su estado.
ObservadorConcreto: Mantiene una referencia a un objeto SujetoConcreto.
Patrón ObservadorProceso
setEstado()
notificar()
actualizar()
getEstado()
actualizar()
getEstado()
unSujetoConcreto unObservadorConcreto otroObservadorConcreto
Observador A Observador B
sujeto
actualizar() actualizar()getEstado()
setEstado()
Patrón ObservadorConsecuencias
Patrón Estrategia
Define una familia de algoritmos, encapsula cada uno de ellos y los hace intercambiables.
Permite que un algoritmo cambie independientemente del cliente que lo utiliza.
Patrón EstrategiaAplicabilidad
Patrón EstrategiaEstructura
Patrón EstrategiaParticipantes
Patrón EstrategiaConsecuencias
Frameworks vs Patrónes de Diseño
Patrónes de Diseño Frameworks
Reutilización de diseño Independiente del
lenguaje Indica como hacer un
buen diseño
Reutilización de diseño y código
Dependiente del lenguaje
Son un diseño en sí mismos
AntiPatronesAntiPatrónes vs Patrónes de diseño
AntiPatrones
AntiPatrones