====== Dessign Patterns ====== ===== Creational Patterns ===== ==== Abstract Factory ==== El patrón //Abstract Factory// se basa en un objeto que clasifica los objetos por famílias y crea dichos objetos según la família demandada. \\ {{ code:dp:abstract.gif |UML}} \\ * //AbstractProductA// y //AbstractProductB// son las abstracciones para los productos A y B, estos dos, a su vez pueden pertenecer a distintas familias: 1 y 2. * //AbstractFactory// es la base para las clases //ConcreFactory1// y //ConcretFactory2//, ambas pueden crear productos A y B, pero cada uno será de distintas famílias. * El cliente sólo interactúa con el //AbstratProductA// y el //AbstractProductB//, para él es intrascendente (trivial, nimio) de qué família provengan. // AbstractFactory interface Factory { Button getButton (); TextBox getTextBox(); } // ConcreteFactory1 class FactoryFramework1 implements AbstractFactory { Button getButton () { // Retorna un botón del framework 1 } TextBox getTextBox () { ... } } // ConcreteFactory2 class FactoryFramework2 implements AbstractFactory { Button getButton () { // Retorna un botón del framework 2 } TextBox getTextBox () { ... } } // AbstractProductA interface Button { public void Click (); } // ConcreteProductA1 class ButtonFramework1 extends GKButton implements Button { public void Click () { this.pushButton(); } } // ConcreteProductB1 class ButtonFramework2 extends QTButton implements Button { public void Click () { this.ClickEvent(); } } // Client Factory fact = new FactoryFramework1 (); Button b = fact.getButton(); // Devolverá el botón de la família demandada (1) * El cliente no tiene por qué tener conocimientos del uso de varias familias y no tiene que tratar con ninguna de ellas, sino con un tipo de datos standard. * Es fácil para el cliente instanciar una nueva familia, pero no la implementación de esta. ==== Builder ==== Separa la construcción del objeto de su interacción con él, para ello se encapsula los métodos de creación en una interface y es a partir de los métodos de esta con la que crearemos el objeto. \\ \\ {{ code:dp:builder.gif |UML}} En el UML... * //Builder// es la especificación de los métodos necesarios para construir un //Product//. * //ConcretBuilder// cada clase que corresponde al objeto constructor de un objeto producto. Hereda de //Builder// e implementando los métodos de //Builder// especifica las distintas propiedades de los objetos //Product//. * //Director// llama a los métodos de un objeto //Builder// que le pasen para construirlo. // Clase Product public class Vehicle { // getters y setters de sus propiedades } // Clase Builder abstract class VehicleBuilder { protected Vehicle vehicle; public VehicleBuilder () { this.vehicle = new Vehicle(); } public Vehicle getVehicle { return this.vehicle; } public abstract void BuildEngine(); public abstract void BuildWheels(); public abstract void BuildDoors(); } // ConcretBuilder 1 class MotorCycleBuilder : VehicleBuilder { public override void BuildEngine() { vehicle.setEngine(500); } public override void BuildWheels() { vehicle.setWheels(2); } public override void BuildDoors() { vehicle.setDoors(0); } } // ConcreteBuilder2 class CarBuilder : VehicleBuilder { public override void BuildEngine() { vehicle.setEngine(2500); } public override void BuildWheels() { vehicle.setWheels(4); } public override void BuildDoors() { vehicle.setDoors(4); } } // Clase Director class Shop { public static Vehicle Construct(VehicleBuilder vehicleBuilder) { vehicleBuilder.BuildEngine(); vehicleBuilder.BuildWheels(); vehicleBuilder.BuildDoors(); return vehicleBuilder.getVehicle(); } } // Construir un coche: Vehicle myCar = Shop.Construct (new CarBuilder()); ==== Factory Method ==== Cuando tenemos que escoger entre varias clases relacionadas para crear un objeto utilizaremos el Factory Method, este se basa en la idea de una creación dinámica, que cambia según las necesidades del objeto que está a punto de crearse. \\ Para ello se utiliza una interfaz (o clase abstracta), llamada //creadora de productos//, que contiene el //método factoría// (factory method) este devuelve objetos del tipo //Producto//, que es una interface de la cual heredan los //productos concretos//. El método factoría, el que devuelve los productos, lo que hace es "configurar" un producto concreto, dando a este las propiedades que lo distinguen de los demás productos concretos. La clave para realizar dicha "configuración" viene dada por las clases //creadoras concretas// que heredan de la creadora de productos y sobreescriben el método factoría, cada una de ellas devuelve en este método el producto concreto que le toca personalizando las propiedades de este. \\ {{ code:dp:factory_method.gif |UML}} \\ Por ejemplo, idearemos un programa que te crea textos concretos (libros, articulos, ensayos, libros de referencia...), para ello configura el texto con diversas secciones. En este caso la interface Seccion sería el //producto//, cada una de las clases que heredasen de Seccion serían los //productos concretos//. La clase abstracta Texto será la //creadora de productos// y cada tipo de textos los creadores concretos. El método factoría sería el //configSecciones//. // Producto interface Seccion { ... }; // Productos concretos class Indice : Seccion { ... }; class Presentacion : Seccion { ... }; class Introduccion : Seccion { ... }; class Capitulo : Seccion { ... }; class Prologo : Seccion { ... }; class Epilogo : Seccion { ... }; class Anexo : Seccion { ... }; // Creadora de productos abstract class Texto { LinkedList secciones = new LinkedList(); public Texto { this.configSecciones (); } public abstract void configSecciones (); }; // Producto concreto: Libro class Libro : Texto { public void configSecciones () { this.secciones.add (new Introduccion ()); this.secciones.add (new Prologo ()); this.secciones.add (new Capitulo ()); this.secciones.add (new Epilogo ()); } } // Producto concreto: Articulo class Articulo : Texto { public void configSecciones () { this.secciones.add (new Indice ()); this.secciones.add (new Introduccion ()); this.secciones.add (new Capitulo ()); } } Utilizaríamos el ejemplo de anterior creando objetos de la siguiente manera: Texto texto1 = new Libro (); Texto texto2 = new Articulo (); ==== Prototype ==== Especifica que la aparición de un nuevo objeto en nuestro escenario programado no tiene por qué crearse como uno nuevo sino que puede ser una copia de otro objeto ya creado previamente. \\ Algunos frameworks ya proveen interfaces que facilitan la implementación de este patrón, por ejemplo Java permite usar la interface //Cloneable// que contiene un método //Clone//. Para implementar el patrón prototype en una clase própia debemos implementar esta interface y sobreescribir este método realizando y devolviendo en él una copia del objeto. \\ \\ {{ code:dp:prototype.gif |UML}} * En según qué casos es más complejo copiar un objeto que volver a crear uno igual. * A veces puede ser más eficiente copiar un objeto que crearlo de nuevo. ==== Singleton ==== Su propósito es asegurar que sólo existe una única instancia de una clase para todo el programa y que esta sea de acceso global. Para ello el acceso al constructor ha de ser privado, o como muy permisivo protegido. La clase deberá de tener de forma estática y privada una instancia al objeto único que existirá y un método estático público que devolverá dicha instancia; de esa forma, el método puede acceder al constructor y al objeto para crearlo y devolverlo (sólo lo creará la primera vez) pero desde fuera de la clase no se podrán crear otros objetos. \\ public class Singleton { private static Singleton INSTANCE = null; private Singleton() {} private synchronized static void createInstance() { if (INSTANCE == null) INSTANCE = new Singleton(); } public static Singleton getInstance() { if (INSTANCE == null) createInstance(); return INSTANCE; } } ===== Structural Patterns ===== ==== Adapter ==== Consiste en adaptar una clase a otra; en otras palabras, modificar una clase (clase adaptada) mediante otra (clase adaptadora) para que otra (clase cliente) la utilice. Sus usos son: * Teniendo una clase que debe ser usada de //una forma especial//, utilizar otra clase para que esta forma de utilización pueda llevarse a cabo. Por ejemplo tenemos una clase linked list (adaptada) que sólo debe almacenar "objetos árboles" de tamaño mayor que 5, para no tener que modificar esta clase y realizar así dicha restricción añadimos otra clase llamada "lista de árboles" (adaptadora), esta tendrá métodos añadir, eliminar... que una linked list tiene y, a la vez, llamará a esos para realizar dichas acciones, aunque //adaptará// el método añadir para filtrar que los árboles introducidos tengan altura mínima 5. * Teniendo una serie de clases (adaptadas) que representan una lista de objetos (pilas, arrays, colas...) y que para utilizarlas de forma uniforme es tedioso (ya sea porque ninguna hereda de una clase en común, porque sus nombres de métodos son distintos...), se crea una clase adaptadora para cada una que facilite la utilización de estas. \\ Existen dos formas de llevar a cabo el patrón adaptador: * //Por herencia//, la clase adaptadora hereda de la clase adaptada y sobreescribe los métodos que quiera modificar. * //Por delegación//, la clase adaptadora contiene un objeto de la clase adaptada, implementa los métodos que quiera adaptar y estos llamarán a los métodos de su objeto. {{ code:dp:adapter.png?650 |UML}} Otro ejemplo. Desarrollamos una librería para dibujar figuras, conocemos como dibujar línias y cuadrados y para ello creamos las clases ''Line'' y ''Square'' que heredan de ''Shape'', para dibujar círculos creamos la clase ''Circle'' pero como no sabemos dibujarlos utilizamos una librería que contiene una clase que los dibuja, la ''XCircle''. Nuestra clase ''Circle'' contendrá un objeto ''XCircle'' y llamará a los métodos de este cuando se llame a los suyos. \\ \\ {{ code:dp:adapterx.png?500 |}} ==== Bridge ==== En un objeto separa la abstracción (interface, clase abstracta de la que hereda) de la implementación (codificación) para agilizar así el cambio de estas independientemente, de esa forma mejora la extensivilidad del código y, a la vez, esconde la implementación a los clientes. \\ {{ code:dp:bridge1.png?650 |Esquema}} \\ En el esquema tenemos una clase abstracta //Window// que tiene un objeto interno del tipo //WindowImp//, este es una interface de la que heredan //XWindowImp// y //PMWindowImp//. Debemos saber con antelación qué motor implementará la ventana (//XWindow// o //PMWindow//), y cuando hagamos alguna acción sobre esta se llamará a los métodos de la instancia //WindowImp// que tiene. Las implementaciones //IconWindow// y //TransientWindow// no son más que tipos de ventana específicos. \\ \\ {{ code:dp:bridge2.png?650 |UML}} ==== Composite ==== Es la cualidad de crear objetos complejos a partir de otros más sencillos y similares, aprovechando una estructura recursiva y arbolea. \\ {{ code:dp:composite2.png?800x200 | UML y Esquema}} * Permite a una serie de objetos ser tratados como uno solo. * Al tener todos estos objetos una interface común pueden ser tratados de la misma manera. * Los objetos tienen una relación de árbol; los objetos pueden contener otros objetos que a su vez pueden contener otros. * Muy usado en el desarrollo de GUI, por ejemplo a un objeto //Ventana// se le añade un objeto //Panel// y a este, a su vez, tiene añadidos otros objetos (cuadros de texto, botones...). \\ {{ code:dp:composite.gif?420x300 |UML}} La función de los elementos que componen este diagrama es: * Component: Es una abstracción (interface o clase abstracta) de un objeto compuesto, define una forma de acceder a los objetos de los que está compuesto (hijos) (puede también añadir una forma para acceder al objeto padre); si es una clase abstracta implementa todos los métodos comunes de un objeto compuesto. * Leaf: Representa el conjunto de hijos, cada uno con su comportamiento concreto. * Composite: Implementan el comportamiento de un componente concreto a parte de almacenar otros componentes hijos e implementar métodos para relacionarlos (añadir, eliminar...). ==== Decorator ==== Añaden funcionalidad extra, dinámica y externamente a una o varias clases. \\ Por ejemplo un objeto cuadro de texto al que queremos asignar unas barras de scroll, estas son otro objeto de otra clase y la funcionalidad que le añaden es la de la movilidad por el texto. El objeto Decorator sería el correspondiente a las barras de scroll, y como tal, debe contener una lista de los objetos a los que afecta; aunque en este caso concreto ha sido únicamente un cuadro de texto podría haber sido un objeto Libro, Vídeo... a los que se les añade la capacidad de ser prestados. \\ * Utilizar objetos decorator para añadir funcionalidad es un método más flexible que la herencia tradicional. * Los objetos pueden ser agregados o desagregados en tiempo de ejecución. * Podemos añadir un objeto decorator a otro (por lo que también tiene que implementar la abstracción que implementan los objetos que manipula). * Un decorator y los objetos que engloba no son idénticos. * A veces añadir un decorator es mejor que manipular las entrañas del objeto, optar por esta segunda opción sería utilizar el patrón Strategy. * Los objetos se deben poder agregar y desagregar de forma sencilla. {{ code:dp:decorator.gif |UML}} ==== Facade ==== En este patrón una única clase representa todo un conjunto de clases y de acciones, la gracia está en que esta clase ha de proveer una forma sencilla de utilizar el sistema al que reemplaza. __Ha de proveer clases sencillas que sirvan para tareas concretas__. \\ Este patrón es útil, por ejemplo, cuando... * Existe un grupo de tareas que se han de realizar frecuentemente, el patrón facade agrupa estas tareas en único método más sencillo y claro que se llamará cuando sea necesario realizar dichas tareas. * Tenemos una API muy útil, pero mal diseñada. Se crea una API intermedia que facilite la utilización de la primera. * Existe un lío de dependencias entre "clientes" y bibliotecas. Se crea dependencia entre clientes -> clase Facade -> biblioteca. * Una biblioteca es dificilmente comprensible. Se crea una intermedia que utiliza esta pero permite realizar sus funciones de una forma más sencilla. {{ code:dp:facade.gif |Esquema}} \\ //* La diferencia entre el patrón adaptador y el facade es que el primero adapta (amplia, mejora, restringe...) un código, mientras que el facade facilita el uso. // ==== Flyweight ==== Su objetivo es optimizar el uso de la memoria agrupando en un objeto (FlyweightObject) propiedades comunes de otros objetos; el FlyweightObject estará contenido como una referencia dentro de los demás que tengan esas propiedades comunes y de igual valor. \\ Por ejemplo, imaginemos 100 objetos //pelota//, cada uno de ellos con un diámetro, un color y una posición en pantalla. De esas 100 pelotas 75 son rojas y de diámetro 4, las otras 25 azules y de diámetro 5; lo único que cambia para cada una de ellas es la posición. El FlyweightObject contendría las propiedades color y diámetro y las pelotas una referencia a un objeto FlyweightObject y un objeto Point que indica la posición, existirían sólo dos objetos FlyweightObject (uno para las rojas de diámetro 4 y otro para las azules de diámetro 5) y 100 objetos pelota con sus 100 objetos Point. \\ Otro ejemplo sería el de carácteres de un párrafo en un procesador de textos, cada carácter contiene un formato concreto (fuente, tamaño, negrita...), pero en general todos contienen las mismas propiedades, es correcto que ellos almacenen un FlyweightObject. \\ * Debemos combinar este patrón con el patrón Factory, el cual se encargaría de gestionar, dar a cada objeto los objetos FlyweightObject y crear nuevos FlyweightObject si son necesarios. ==== Proxy ==== Se utiliza un intermediario entre varios objetos para controlar el acceso de uno a otro. Para ello el cliente tiene acceso a un objeto //Proxy// que enmascara otro objeto, el que realmente está utilizando el cliente, es decir, dentro del //Proxy// se llaman a los métodos del objeto real permitiendo\impidiendo que se realicen acciones sobre este. * Se impide que un objeto que puede ocupar gran espacio en memoria se cree o duplique innecesariamente. {{ code:dp:proxy.png |UML}} \\ El patrón //Proxy// permite las siguientes variantes: * //Proxy remoto//: Cuando provee acceso a otro objeto localizado en otra porción de memoria o máquina. * //Proxy virtual//: Realiza las operaciones de otro objeto para que este no se cree a no ser que sea necesario. * //Copy-on-write proxy//: Retrasa la clonación de un objeto hasta que no sea realmente necesaria (proxy virtual). * //Proxy de protección//: Distingue entre tipos de clientes para darles distintos niveles de acceso. * //Proxy con caché//: Almacena temporalmente resultados de operaciones de alto coste y las comparte con varios clientes. * //Firewall-Proxy//: Protege un objeto de operaciones que otro realice. * //Proxy de sincronización//: Provee control de concurrencia entre objetos. * //Proxy inteligente//: Provee acciones adicionales del objeto que referencia (como indicar número de copias existentes...). === Proxy Virtual === // Subject interface Image { public void displayImage(); } // RealSubject class RealImage implements Image { private String filename; public RealImage(String filename) { this.filename = filename; System.out.println("Cargando "+ filename); } public void displayImage() { System.out.println("Displaying "+ filename); } } // Proxy class ProxyImage implements Image { private String filename; private Image image; public ProxyImage(String filename) { this.filename = filename; } public void displayImage() { if (image == null) { image = new RealImage(filename); // load only on demand } image.displayImage(); } } class Client { public static void main(String[] args) { List images = new ArrayList(); images.add( new ProxyImage("HiRes_10MB_Photo1") ); images.add( new ProxyImage("HiRes_10MB_Photo2") ); images.add( new ProxyImage("HiRes_10MB_Photo3") ); images.get(0).displayImage(); images.get(1).displayImage(); images.get(0).displayImage(); } } /* 1. Se carga la primera imágen y se muestra. 2. Se carga la segunda imágen y se muesra. 3. Se muestra la primera imágen ya cargada. 4. La tercera imágen no llega a cargarse nunca. === Proxy de protección === public interface IClient{ string Login(); } public class RealClient : IClient{ public string Login(){ return "Logged"; } } public class ProtectionProxy : IClient { private string password=null; RealClient client=null; public ProtectionProxy(string pwd) { Console.WriteLine("ProtectionProxy: Initialized"); password=pwd; } public String Login() { if(tmpPwd == password) { client=new RealClient(); return "Successfull!"; } else return "Fail!"; } } class ProtectionProxyExample { public static void Main(string[] args) { IClient client = new ProtectionProxy("thePassword"); Console.WriteLine(client.Login()); } } ===== Behavioral Patterns ===== ==== Chain of Responsibility ==== Se basa en una serie de objetos por la que circula una petición realizada a un objeto inicial, tras recibirla este la pasa al siguiente objeto y así irá pasándose hasta llegar al objeto adecuado para procesarla. Todos estos objetos receptores derivarán de una misma interface o clase abstracta, que provea un método para obtener el sucesor, esto hará que la cadena sea más flexible y transparente. \\ Existen varias técnicas para desarrollar esta cadena: * Aprovechando enlaces ya creados, como por ejemplo las relaciones padre-hijo que un patrón composite implementa. * Si no existiesen enlaces deberíamos crear uno conectando los objetos. Una forma sería haciendolos derivar de una misma interface, esta debe de tener internamente un objeto que también deriva de ese mismo tipo el cual representaría el siguiente en la cadena. {{ code:dp:chain_responsability.gif |UML}} // Handler public abstract class Empleado { Empleado sucesor; public void setSucesor (Empleado e) { this.sucesor = e; } public void ReceivePeticion (Peticion pet) { if (Condition(pet)) Process (pet); else { if (this.sucesor != null) this.sucesor.ReceivePeticion(pet); else // Gestión de la petición si no existe sucesor } } abstract void Process (Peticion pet); abstract boolean Condition (Peticion pet); } // Concrete Handler1 public class Director extends Empleado { boolean Condition (Peticion pet) { return (pet.dinero >= 5000); } void Process (Peticion pet) { // Proceso de una petición para el director } } // Concrete Handler2 public class Gerente extends Empleado { boolean Condition (Peticion pet) { return (pet.dinero >= 2500); } void Process (Peticion pet) { // Proceso de una petición para el gerente } } // Deben ser asignados los sucesores Gerente ger = new Gerente(); Director dir = new Director(); dir.setSucesor(ger); * Se simplifica la estructura interna del objeto, cada objeto se encarga de un problema concreto. * Se añade flexibilidad, distribuyendose la responsabilidad entre varios objetos. * Se desvincula al emisor de la petición del receptor. * Se debe debe decidir si se realizará un control para toda petición sea procesada; en definitiva, si se controlará qué ocurre si no llega a existir ningún elemento de la cadena encargado de un tipo de peticiones concreto (este control puede ser una ventaja o desventaja según se mire). ==== Command ==== Utiliza las acciones de los objetos como otros objetos, de esa forma nos permite clasificar las peticiones, registrarlas y hasta deshacerlas. \\ \\ {{ code:dp:command.gif |UML}} \\ * //Command// es la interface para cualquier acción\operación. Las operaciones en concreto son las //ConcretCommand//, ellas almacenan un objeto //Receiber// este recibirá la acción cuando se llame al método //Execute// declarado en //Command//. * //Invoquer// será el encargado de hacer que el comando se ejecute, colocará todo comando que vaya lanzando en una pila, de esa forma se implementará la capacidad de hacer\rehacer. * //Client// crea un //ConcretComman// y le asgna un //Receiber//. interface Command { public Number Execute (Number n); public Number Undo (Number n); } // Receiver class Number { int value = 0; public Number (int v) { this.value = v; } public int getValue () { return this.value; } } class Invoker { int idx = 0; Stack commands; Number vActual; public void Execute (char operation, Number n) { Command c; switch (operation) { case '+': c = new Suma (n); break; case '-': c = new Resta (n); break; } vActual = c.Execute(); commands.Push(c); idx++; } public void Undo () { idx--; Command c = commands.Pop(); vActual = c.Undo(vActual); } } // ConcreteCommand public class Suma implements Command { Number value; public Suma (Number n) { this.value = n; } public Number Execute (Number n) { return new Number(this.value.getValue() + n.getValue()); } public Number Undo (Number n) { return new Number(n.getValue() - this.value.getValue()); } } Este patrón nos proporciona: * Capacidad de deshacer acciones, cuando un usuario quiere deshacer uno de los comandos lanzados el programa hará un pop de la pila de comandos sacanda el último y ejecutará su método //undo//. * Capacidad de rehacer; si se ejecuta un método deshacer se puede devolver el //command// contrario (un command suma pasa a ser un command resta) y cuando se pide rehacer volvería a pasarse el contrario, el primero (el //command// resta devolvería un suma). * Se facilita la creación de un algoritmo //commit-rollback//. * Facilita la creación de //wizards// (asistentes). Cada página de este contiene un objeto //command// que se almacena en una cola a medida que las páginas van pasando, una vez se llegue al final se llamarán a los métodos //execute// de cada uno de estos //command//. * Facilidad para la creación de macros, si se almacenan los objetos //command// lanzados luego pueden volver a ser lanzados de nuevo de forma secuencial. * Podemos traducir cada //command// a una o varias sentencias de script. (De command a script y viceversa). * Podemos enviar objetos //command// en red y estos serán ejecutados en otra máquina objetivo. ==== Interpreter ==== Su idea básica es la especificación e implementación un sub-lenguaje que resuelva rápidamente un tipo de problemas dinámicos. \\ Por ejemplo, desarrollar una clase interprete para expresiones matemáticas, esta recibiría en el constructor un String con la expresión a calcular y tendría un método "Solution" que procesaría dicha expresión y retornaría un resultado. Para procesar la expresión se ayudaría de clases que representasen los números, los signos (una para cada signo, realizando cada una la acción concreta con los objetos número)... * La gramática del lenguaje interpretado no debe ser compleja. * Tal vez tengas que utilizar árboles sintácticos o [[otros:regex|expresiones regulares]]. * Para la solución del problema la eficiencia\rapidez no es un factor importante. * Es fácil extender y ampliar la gramática, ya que el intérprete usa clases para representar las reglas. * Puede que necesites otros patrones para ampliar funcionalidades del interprete o facilitar su función: Composite (para manejar el árbol sintáctico), Flyweight (para dividir los símbolos en el árbol sintáctico), Iterator (para manejar independientemente los elementos) o Visitor (para mantener\ampliar el comportamiento de un árbol con una sola clase). ==== Iterator ==== Se basa en utilizar una interface que defina los métodos adecuados para realizar acciones sobre listas (recorrer secuencialmente y posicionarse en sus elementos (siguiente, anterior, primero, ultimo), añadirlos, eliminarlos...); todas las clases que correspondan a estas listas deberán implementar la interface, de esa forma todas las listas de objetos se tratarán igual aunque su estructura interna (pila, cola, hash list...) sea totalmente distinta. * Un método de la interface Iterator deberá retornar un objeto de este mismo tipo, la clase que lo implemente retornará un puntero a 'this' en ese método. * Podemos crear una clase adaptadora que contenga una lista e implemente la interface Iterator. ==== Mediator ==== Define una comunicación simplificada entre clases, para ello en un objeto encapsula la forma en la que otros interactúan. \\ Cuando en un escenario un gran número de clases interactúan entre sí, haciendo que unas cambien el estado de las otras, puede llegarse a una estructura muy compleja. Además, la reutilización y personalización de los objetos se complica debido a que existe una gran interdependencia entre ellos y es complejo separarlos del del grupo. Necesitamos un mediator! \\ \\ {{ code:dp:mediator.gif |UML}} \\ * //Mediator// es una interface para comunicar objetos //Colleage//. * //ConcreteMediator// es el encargado de gestionar la comunicación entre objetos //Colleage//, implementa los métodos de //Mediator//. * //Colleague// es la interface que implementan los //ConcreteColleague//, esta ha de permitir a estos acceder a un //Mediator//. Se comunicarán gracias a este. \\ Existen varias formas de implementar un Mediator: * Podemos omitir la interface //Mediator// si todos los objetos van a comunicarse con un solo mediador. * Podemos implementar este patrón como un patrón Observer, //ConcretMediator// "observa" los cambios en los //ConcreteColleague//. * El objeto //ConcretMediator// puede realizar acciones sobre los objetos //ConcreteColleague// cuando uno de ellos hace una petición, acciones como cámbio de estado, actualización de valores... ==== Memento ==== Captura externamente el estado de un objeto para luego restaurarlo, y esto sin violar las reglas de encapsulación. Se almacenará el estado en una clase independiente a la del objeto a restaurar. Implementaciones alternativas: * Utilizar dos métodos "getState" y "restoreState", el primero devolvería un objeto con el estado actual y el otro, pasándole este estado, lo restauraría. * En Java, utilizar la serialización. {{ code:dp:memento.gif |UML}} * //Memento// almacena el estado del objeto //Originator// y protege el acceso contra otros objetos que no sean de este tipo. * //Caretaker// almacena mementos, no los examina ni opera con ellos, su único fin es devolver el memento adecuado al //Originator// adecuado. * //Originator// crea un memento con el estado actual. También es capaz de volver a un estado anterior a partir de un memento. ==== Observer ==== Provee una forma de que un objeto avise a otros que ha cambiado de estado, y así estos puedan actualizarse automáticamente y autónomamente, teniendo los dos tipos de objetos una relación nula (el que avisa no tiene por qué conocer a los receptores). El objeto que cambia de estado y avisa es el observado y los que reciben el aviso que ha cambiado los observadores. * Es un patrón muy adecuado para implementar eventos. * Evita bucles que revisan un objeto innecesaria y continuamente. \\ {{ code:dp:observer.gif |UML}} * Tenemos una interface //Observer// que hace que las clases que la implementen codifiquen el método //Update//; este método es el que se llama cuando se recibe un aviso de cambio de estado del observable. * Una clase abstracta //Subject// (o //Observable//) y una clase //ConcreteSubject// que hereda de la //Subject//. //Subject// deberá guardar una lista de los observadores (los que implementan //Observer//), de la misma forma que tendrá un método //Notify// que será llamado cuando el //ConcreteSubject// cambie y este, lo que hará, será recorrer todos los observadores y llamar a sus métodos //Update//. Deberá también implementar métodos que permitan añadir y eliminar observadores. * Los //ConcreteObserver// (objetos que observan al observable), heredarán de //Observer// y tendrán una instancia del objeto //ConcreteSubject// pero no será necesario que la examinen hasta que no se llame al método //Update//. public abstract class Subject { LinkedList listObservers; ... public void AddObserver (Observer) {...} public void DeleteObserver (Observer) {...} public Notify () { for (Observer o : listObservers) o.Update(); } } public class Pelota extends Subject { Point point; ... void Mover (int x, int y) { point = new Point (x, y); this.Notify(); } } public class Espectador implements Observer { Pelota p; ... void Update () { MirarPosicion(p.getPoint()); } } ==== State ==== Cambia el comportamiento del objeto cuando su estado cambia. Para lograrlo crea un objeto por cada estado, codificando los métodos adecuados para cada estado concreto, el objeto que cambia de estado almacena una instancia a este tipo de objeto estado y actuará según le dicte este objeto. {{ code:dp:state.gif |UML}} \\ * La clase //Context// es la que cambia de estado. Contiene una instancia a //ConcreteState// definiendola del tipo State. * //State// es la interface de la que heredan los distintos estados //ConcreteState//. * Es necesario comprender que el cliente contendría la clase //Context//, que es la que internamente cambia de estado, como si esta cambiase dinámicamente de implementación. public interface State { public void setVelocity (int vel); public int getGasto (); } public class stateStop implements State { private Car the_car; public stateStop (Car c) { this.the_car = c; } public void setVelocity (int vel) { if (vel > 0) the_car.setState(new stateRun()); } public int getGasto () { return 0; } } public class stateRun implements State { private Car the_car; public stateRun (Car c) { this.the_car = c; } public void setVelocity (int vel) { if (vel <= 0) the_car.setState(new stateStop()); } public int getGasto () { return 1; } } public class Car { State state; int gasolina = 100; public Car () { state = new stateStop(this); } public void setState(State s) { this.state = s; } // A este método se llama una vez por minuto. public void gastar () { this.gasolina -= this.state.getGasto(); } } En este ejemplo se muestra una de las dos posibles implementaciones para el cambio de estado, el estado es quien elige cual será el siguiente cambio de estado. Otra forma de realizar la transición entre estados podría ser la implementación en la misma clase //Context//. ==== Strategy ==== Se basa en la óptima utilización de métodos abstractos y de la implementación de la herencia de las clases que los contienen. \\ Una clase (A) con un método abstracto, el ejecutor de la estrategia, define varias estratégias según las clases (E1, E2, E3...) que heredan de esta e implementan dicho método. Otra clase (B) que quiera utilizar una de estas estrategias definirá un objeto del primer tipo (A) como uno nuevo de sus herederos (E1, E2, E3...) y llamará al método de la estrategia. // E1, E2 y E3 heredan de A A obj1 = new E1(); A obj2 = new E2(); A obj3 = new E3(); // A contiene el método abstracto strategy que E1, E2 y E3 han implementado obj1.strategy(); obj2.strategy(); obj3.strategy(); ==== Template Method ==== En una clase abstracta que contiene un método para realizar un algoritmo y este, a su vez, utiliza otros métodos abstractos de esa clase para realizar dicho algoritmo; en las clases que hereden de esta se podrán sobreescribir estos métodos (o deberán si se implementan como abstractos) para cambiar el algoritmo. {{ code:dp:template.gif |UML}} \\ Los pasos para implementar el //TemplateMethod// son los métodos //PrimitiveOperation1// y //PrimitiveOperation2//, el algoritmo cambiará una vez los cambiemos. ==== Visitor ==== Permite una nueva operación a una clase sin efectuar ningún cambio en esta. \\ Existe un objeto (//ObjectStructure en el UML//) que contiene una colección de elementos //visitables// (//Element en el UML//), a este se le pasa un objeto //visitador// (//en el UML el visitador es un objeto ConcretVisitor que hereda de Visitor//). El objeto al que le ha llegado el visitador recorre todos los elementos de la lista y se los pasa al visitador para que realice su //visita// (//en el UML esta corresponde al método VisitConcretElement//). Una visista consiste en la nueva operación que queremos implementar sobre los objetos visitables. \\ También puede ser implementado mediante un patrón composite, ya que para implementar este patrón añadiendo nueva funcionalidad a la estructura esta no ha de sufrir cambio alguno para poder realizar las //visitas//. \\ \\ {{ code:dp:visitor.gif |UML}} \\ // Interfaces public interface Visitor { public void visit (Visitable v); } public interface Visitable { public void accept (Visitor visitador); } // Element public abstract class GeometricRegularFigure implements Visitable { protected int nLados; protected int sizeLado; public void accept (Visitor visitador) { visitador.visit(this); } public int getNLados() { return nLados; } public int getSizeLado() { return sizeLado; } } // ConcreteElement1 (Visitable) public class Square extends GeometricRegularFigure { public Square (int size) { this.nLados = 4; this.sizeLado = size; } } // ConcreteElement2 (Visitable) public class Triangle extends GeometricRegularFigure{ public Triangle (int size) { this.sizeLado = size; this.nLados = 3; } } // ObjectStructure public class ContainerFigures { private ArrayList figures; public ContainerFigures() { this.figures = new ArrayList(); } public void Add (GeometricRegularFigure f) { this.figures.add(f); } public void realizaVisitas (Visitor visitador) { for (GeometricRegularFigure grf : figures) visitador.visit(grf); } } // Visitador public class PerimeterCalculator implements Visitor { private int perimeter; public PerimeterCalculator() { this.perimeter = 0; } public void visit(Visitable v) { this.perimeter += (((GeometricRegularFigure)v).getNLados() * ((GeometricRegularFigure)v).getSizeLado()); } public int getCalculo () { return this.perimeter; } } // Cliente ContainerFigures coleccion = new ContainerFigures(); coleccion.Add(new visitorATD.Triangle(5)); coleccion.Add(new visitorATD.Triangle(4)); coleccion.Add(new visitorATD.Square(15)); coleccion.Add(new visitorATD.Square(5)); PerimeterCalculator pc = new PerimeterCalculator(); coleccion.realizaVisitas(pc); ====== Documentos ====== {{ code:dp:designpatterns1.jpg?700px |}} {{ code:dp:designpatterns2.jpg?700px |}} {{code:dp:designpatternscard.pdf|}}