Command Nombre del patrón: command –orden (también es conocido como Action y Transaction) Clasificación del patrón: Patrones de comportamiento. Problema: Necesidad de enviar peticiones sin saber nada acerca de la operación solicitada ni del recepetor de las mismas (toolkits de interfaces de usuario) Cola de comandos (control de la secuencia) Operaciones con históricos Contexto: Implementación de un sistema basado en órdenes (comandos). Prioridades: Uniformidad al invocar acciones Sencillez para extender el sistema con nuevas acciones. Objetivos: Cada elemento de menú que invoca una acción no debe conocer el método que implementa ni la clase concreta que la implementa. Encapsular la petición como un objeto, permitiendo parametrizar los clientes con distintas peticiones, mantener una cola de peticiones y deshacer operaciones. Estructura: Participantes: Orden: Declara una interfaz para la ejecución de órdenes. OrdenConcreta: Define un enlace entre un objeto Receptor y una acción. Implementa Ejecutar invocando la correspondiente operación u operaciones del Receptor. Cliente: Crea un objeto OrdenConcreta y establece su receptor. Invocador: Le pide a la orden que ejecute la petición Receptor: Sabe cómo llevar a cabo las operaciones asociadas a una petición. Cualquier clase puede actuar como Receptor. Colaboraciones: El cliente crea un objeto OrdenConcreta y especifica su receptor. Un objeto Invocador almacena el objeto OrdenConcreta. El Invocador envía una petición llamando a Ejecutar sobre la orden. Cuando las órdenes se pueden Deshacer, OrdenConcreta guarda el estado para deshacer la orden antes de llamar a Ejecutar. El objeto OrdenConcreta invoca operaciones de su receptor para llevar a cabo la petición. Ventajas: Permite el desacoplamiento usando el objeto Orden ya que el invocador no es quien realiza la acción. Se pueden crear operaciones que invoquen a varias acciones. Permite cambiar de forma dinámica las acciones que es capaz de invocar un menú. Se puede deshacer las acciones. Inconvenientes: Aumenta la complejidad al incrementar el número de clases (las cuales no tienen gran responsabilidad) Patrones relacionados Composite: para agrupar órdenes. Prototype: para copiar la orden al historial de órdenes. Factory Method: forma alternativa de llamar a las órdenes Memento: mantener el estado de los objetos para implementar la orden Deshacer. Template: para implementar la lógica de Deshacer de forma automática. Interpreter: se puede implementar un intérprete a través de órdenes command. Ejemplo publicinterface Orden { voidejecutar(); } publicclassInsertarCuadradoimplementsOrden{ Imagen imagen; publicInsertarCuadrado(Imagen imagen){ this.imagen=imagen; } publicvoidejecutar() { imagen.generarCuadrado(); } } publicclassMenuDibujo{ publicbooleanMenuPrincipal() { ... case 2: //dibujar un cuadradoCommandcomando; comando= new InsertarCuadrado(imagen); comando.ejecutar(); break; ... } } Composite Nombre del patrón: Composite (Compuesto) Clasificación del patrón: Diseño Estructural de Objetos. Problema Estructuras de árbol o estructuras 1:N Existencia de un objeto complejo que hay que descomponer en partes Nodos especiales que pueden contener otros nodos Contexto: Las aplicaciones (gráficas, editores de dibujo, diseño de circuitos) donde los usuarios necesitan construir diagramas complejos con componentes simples. Dichos componentes pueden ser agrupados para formar otros más grandes, y estas a su vez pueden ser agrupadas. El patrón Composite describe cómo usar una composición recursiva para no tener que distinguir entre componentes simples y compuestos. Objetivo: Componer objetos en estructura de árbol para representar jerarquías de tipo “partestodo”. Permitir que los clientes traten objetos individuales o primitivos y composiciones de objetos de manera uniforme. Participantes: Componente (Gráfico): Declara la interfaz de los objetos de la composición Implementa el comportamiento predeterminado de la interfaz, común a todas las clases Declara una interfaz para acceso y manipulación a sus componentes hijos Define una interfaz para acceder al padre de un componente en la estructura recursiva (Opcional) Hoja (Rectángulo, Línea, Texto): Representa los objetos “Hoja” en la composición (no poseen hijos) Define el comportamiento de los objetos primitivos Composite (Dibujo) Define el comportamiento de los componentes que tienen hijos Almacena componentes hijos Implementa las operaciones de la interfaz Componente relacionada con los hijos Cliente Manipula objetos de la composición a través de la interfaz Componente. Colaboraciones Los clientes usan la interfaz Componente para interactuar con los objetos de la estructura Composite. Si el receptor es una hoja, la interacción es directa y la petición se trata correctamente. Si es un Compuesto, se debe llegar a los objetos “hijos”, entonces, normalmente se redirigen las peticiones a sus componentes hijos, posiblemente utilizando operaciones adicionales. Ventajas Define jerarquía de clases formadas por objetos primitivos y compuestos Simplifica el cliente. Tratan uniformemente estructuras compuestas y objetos individuales Facilita añadir nuevos tipos de componentes Inconvenientes Hace el diseño demasiado general Hay que tener más cuidado con las restricciones en los tipos de objetos que forman parte de un compuesto Hay que realizar comprobaciones dinámicas en tiempo de ejecución Patrones relacionados Chain of Responsibility. Hace uso del enlace al componente padre, para crear enlaces entre la clase componente y su padre. Decorator. Juntos, tienen una clase padre común, por lo que el patrón decorator tiene que admitir la interfaz Componente con operaciones como Añadir, Eliminar y Obtener hijos. FlyWeight. Permite compartir componentes, en cuyo caso ya no pueden referirse a sus padres. Iterator. Para recorrer las estructuras definidas por el patrón Composite. Visitor. Para localizar operaciones y comportamiento que de otro modo estarían distribuidas en varias clases Compuesto y Hoja Implementación: 1. Referencias explícitas a los padres. Simplifica recorridos y la gestión de una estructura compuesta Facilita ascender en la estructura y borrar un componente ¿Dónde? En la clase Componente 2. Compartir componentes. Ahorra espacio de almacenamiento Es difícil compartir cuando el componente tiene un solo padre Solución Patrón Flyweight 3. Maximizar la interfaz Component. Es bueno definir más operaciones posibles en la clase componente tanto para Compuesto como para Hoja Define operaciones por defecto en Componente y luego las subclases puedan redefinirlas si son necesarias. 4. Declarar las operaciones de manejo de hijos. En la raíz de la jerarquía (Componente) Da transparencia y pierde seguridad ¿Cómo asegurarse de que no se le solicita una operación no permitida a un nodo hoja? Ej. Añadir y eliminar objetos de las hojas En la clase Compuesto Da seguridad pero pierde transparencia. Cualquier intento de añadir o eliminar objetos de las hojas se detecta en tiempo de compilación Las hojas y los compuestos tienen interfaces diferentes Implementación: 1. ¿Debería Componente implementar una lista de componentes? Penaliza el espacio Solo merece la pena si hay relativamente pocos hijos 2. Orden de los hijos. Colaboración del patrón Iterator. 3. Caché para mejorar el rendimiento. 4. ¿Quién debería borrar los componentes? Sin recolector de basura Compuesto responsable de borrar sus hijos 5. ¿Cuál es la mejor estructura de datos para almacenar componentes? Listas enlazadas, árboles, arrays y tablas de dispersión Eficiencia Ejemplo Facade Nombre del patrón: Facade, Façade (Fachada) Clasificación del patrón: Estructural. Intención: Proporciona una interfaz unificada para un conjunto de interfaces en un subsistema. Define una interfaz de alto nivel que hace que el subsistema sea más fácil de usar. Motivación: Estructurar un sistema en subsistemas ayuda a reducir la complejidad. Minimizar la comunicación y las dependencias entre subsistemas. Aplicabilidad: Proporcionar una interfaz simple para un subsistema complejo. Gran cantidad de dependencias entre los clientes y las clases que implementan una abstracción. Dividir los subsistemas en capas. Estructura: Participantes: Fachada: Sabe qué clases del subsistema son las responsables ante una petición. Delega las peticiones de los clientes en los objetos apropiados del subsistema. Clases del subsistema: Implementan la funcionalidad del sistema. Realizan las labores encomendadas por el objeto Fachada. No conocen a la fachada, no tienen referencias a ella Colaboraciones: Los clientes se comunican con el subsistema enviando peticiones al objeto Fachada, que las redirecciona a los objetos correspondientes del subsistema. Los clientes que usan la fachada no tiene que acceder directamente a los objetos del subsistema. Consecuencias: Oculta a los clientes los componentes del subsistema. Promueve un débil acoplamiento entre el subsistema y los clientes. No impide que las aplicaciones usen las clases del subsistema si es necesario. Implementación: Reducción del acoplamiento cliente-subsistema. Clases del subsistema públicas o privadas. Código de ejemplo: import java.util.*; /** "Facade" */ class UserfriendlyDate { GregorianCalendar gcal; public UserfriendlyDate(String isodate_ymd) { String[] a = isodate_ymd.split("-"); gcal = new GregorianCalendar(Integer.valueOf(a[0]).intValue(), Integer.valueOf(a[1]).intValue()-1, Integer.valueOf(a[2]).intValue()); } public void addDays(int days) { gcal.add(Calendar.DAY_OF_MONTH, days); } public String toString() { return new Formatter().format("%1$tY-%1$tm-%1$td", gcal).toString(); } } /** "Client" */ class FacadePattern { public static void main(String[] args) { UserfriendlyDate d = new UserfriendlyDate("1980-08-20"); System.out.println("Date: "+d); d.addDays(20); System.out.println("20 days after: "+d); } } Usos conocidos: Clases java.awt.Font y java.awt.Graphics. Interfaces de librerias GIS o librerías matemáticas. Patrones relacionados: Abstract Factory Mediator Singleton Method Factory Nombre del patrón: Method Factory (Método Factoría) Clasificación del patrón: De Creación. Problema: Creaciones de objetos por todo el código Contexto: Aplicaciones (frameworks) que se desean mantener flexibles y extendibles Prioridades: Flexibilidad. No aumente complejidad. Objetivo: Tener localizado la creación del código. Pueda extenderse para crear clases no creadas aún. Ejemplo del Patrón ● Librería de clase log, para depuración. ● Distintos tipos de gestión de mensajes (en pantalla, en fichero log o mediante un evento a un proceso). ● Por su diferencia implementación, se implementan en clases distintas. Se puede trabajar con clase abstracta. Se escriben los mensajes sin saber la recepción. ¿Cómo se crea el log concreto? Si cada componente lo crea no se puede gestionar. No se podrían emplear clases Log diseñadas después. Idea: Delegar la creación a una clase abstracta. Ejemplo de uso void iniciaAplicación(LogFactory *factory) { Log *log = factory.creaLog('MAIN'); log> escribeMsgDepuración(“Iniciando sistema”); SistemaAutentificacion autentifica(factory> creaLog('AUTENTIFICACION') ); ServidorPeticiones peticiones (autentifica, factory> creaLog('SERVIDOR') ); log> escribeMsgDepuración(“Empiezo a atender peticiones”); peticiones.atiendePeticiones(); } Participantes: Factory o Creator: Define el método de creación. Abstract Creator: Se define por cada clase a crear. Ventajas: Permite extender el modelo con herencia (ej: GZipLog) No se especifican clases concretas en el código. Permite generar jerarquías paralelas Inconvenientes: Mayor complejidad: Aumenta el número de clases Patrones relacionados Singleton, puede usarse para crear sólo un objeto Factoría (peligro en su uso). Ofrece una misma solución al problema que el patrón Prototipo Builder, Cuando la complejidad de creación aumenta Variante del Patrón: Parametrizado Hace uso de switch, no de herencia. Encapsula, pero no permite extender. getLog(string name) { Properties config(“config”); String type = config.getProperties(name +”.type”); if (type.equals(“file”)) { return new FicheroLog(name); } else if (type,equals(“console”)) { return new ConsolaLog(name); } ... Decorator Nombre del patrón: Decorator (Decorador) Clasificación del patrón: Estructural. Problema Este patrón nos permite asignar dinámicamente propiedades y comportamientos a objetos individuales y no a toda una clase Contexto Añadir propiedades y/o funcionalidades a objetos de una interfaz de usuario. Prioridades: El Decorator se puede aplicar de forma recursiva para añadirle nuevas responsabilidades permitiendo una mayor flexibilidad de la funcionalidad del objeto. Objetivo: Añadir objetos individuales de forma dinámica y transparente para no afectar a otros objetos. Las funcionalidades no tienen por que ser aplicadas. Cuando no se puede usar herencia por tener una gran número de ellas para cubrir todas las combinaciones. Estructura: Participantes: Componente: Interfaz para objetos donde añadir funcionalidad dinámicamente. Operacion() : Método abstracto heredado por la clase ComponenteConcreto. ComponenteConcreto: Objeto al que añadir nuevas funcionalidades mediante la clase Decorator. Operacion() : Método que le da una funcionalidad a la clase. Decorador: Interfaz que se ajusta al interfaz componente. Operacion() : Método abstracto heredado de la clase Componente. DecoradorConcretoA: Añade funcionalidades al componente. Operacion() : Método que llama a las funcionalidades de la clase. ComportamientoAñadidoA() : Método que le da una funcionalidad dinámica a la clase. AtributoA: Atributo usado por el método ComportamientoAñadidoA. Ventajas: Flexibilidad en comparación con la herencia estática. Evita sobrecargar de funcionalidad las clases padres de la jerarquía. Inconvenientes: Muchos objetos pequeños que complican su aprendizaje y depuración El decorador y sus componentes no son idénticos. No se puede confiar en que ambos sean iguales. Patrones relacionados Adapter: Proporciona una interfaz nueva mientras que el Decorator solo le da funcionalidades nuevas. Composite: Proporciona objetos nuevos mientras que el Decorator solo añade funcionalidades. Strategy: Cambia la estructura interna de un objeto mientras que el Decorator modifica su exterior. Ejemplo Iterator Nombre del patrón: Iterator (Iterador) Clasificación del patrón: Comportamiento. Problema: Nos permite acceder secuencialmente a los elementos de un objeto agregado sin modificar la funcionalidad interna. Contexto: El Iterator es una interfaz que tiene la funcionalidad de acceder y recorrer cada uno de los elementos del objeto agregado. Objetivo: Proporciona una interfaz uniforme para recorrer distintas estructuras agregadas (iteración polimórficas). Permite realizar diferentes recorridos sobre objetos agregados Separa el mecanismo de recorrido del objeto “Lista” permitiendo iteradores con diferentes políticas de recorrido sin necesidad de enumerarlos en la interfaz de “Lista” Estructura: Participantes: IteradorConcreto: Implementa la interfaz Iterator y mantiene la posición actual en el recorrido del agregado. Iterador: Interfaz para recorrer los elementos y acceder a ellos. Primero(): Accede al primer elemento. Siguiente(): Accede al próximo elemento. HaTerminado(): Comprueba el fin del agregado. ElementoActual(): Hacer referencia al elemento actual. Agregado: Define una interfaz para crear un objeto iterador. CrearIterador() : Método abstracto. AgregadoConcreto: Implementación del interfaz de creación de Iterator. CrearIterador() : retorna una instancia del iterador. Ventajas: Soporte a variaciones en el recorrido de un agregado. Los iteradores hacen fácil cambiar el algoritmo de recorrido, con sólo reemplazar la instancia del iterador a una diferente. Los iteradores simplifican la interfaz de los agregados, ya que la interfaz de los recorridos se encuentra en los iteradores y no en los agregados. Más de un recorrido puede estar pendiente en un agregado, puesto que cada iterador mantiene la pista de su recorrido. Inconvenientes: Puede existir una interferencia por parte de las actualizaciones durante un recorrido secuencial. Patrones relacionados Composite: Se suele usar en estructuras recursivas. Factory: Los iteradores polimórficos se basan en este método. Patrones Colaboradores: Mementos: Se usa para representar el estado de una iteración. Interface Lista template <class Elemento> class Lista { public: Lista(long tamanio = MAX_LISTA); long Contar() const; Elemento& Obtener(long indice) const; … }; Implementación lista<Animal> animales; Animal animal; … lista<Animal>::Iterador it; cout << “Animales: “; it = animales.Primero(); while (! animales.HaTerminado()) { animal = *it; cout << “,” << animal; animales.Siguiente(); } cout << endl; Interface Iterador template <class Elemento> class Iterador { public: virtual void Primero(); virtual void Siguiente(); virtual bool HaTerminado() cosnt = 0; virtual Elemento ElementoActual() cosnt = 0; protected: Iterador(); }; Strategy Nombre del patrón: Strategy (Estrategia) Clasificación del patrón: Comportamiento Problema: Existen diversas estrategias para abordar un problema. Contexto: Se tiene un problema que puede ser implementado o afrontado de distintas formas y cuyo interfaz esté bien definido y es común para dichas formas, pudiendo ser cualquiera de ellas válida o más deseable en determinadas situaciones y permitiendo el cambio entre las distintas estrategias en tiempo de ejecución. Prioridades: Reducir complejidad sistema (número de clases). Eficiencia (elección de la mejor estrategia icluso en tiempo de ejecución) Flexibilidad (si se necesitara una nueva estrategía tan solo habría que implementar la interfaz adecuadamente). Objetivo: Encapsular funcionalidad en forma de objetos, los cuales puedan ser modificados de forma dinámica por los clientes. Estructura: Participantes: Contexto (Context): Clase que controla el acceso a las diferentes estragias. Estrategia (Strategy): Clase que define la interfaz común a todas las estrategias. EstrategiaConcretaX (ConcreteStrategyX): Clase que implementa una de las posibles estrategias. Ventajas: Facilidad de agregar una nueva opción dinámicamente. Encapsulamiento de las estrategías. Se pueden añadir nuevas implementaciones y quitar o modificar las existentes de una forma muy limpia y eficiente. Reducción número de clases. Una única clase utiliza diferentes estrategias (comportamientos) . Inconvenientes: Incremento del número de objetos. Sobrecarga de la comunicación entre la clase Contexto y Strategy sin importar la complejidad de la estrategia. No es deseable que el cliente decida que estrategia seguir. Patrones relacionados Bridge: Tiene el mismo diagrama UML, pero se diferencia en sus “intenciones”: Strategy, indica el comportamiento; Bridge, la estructura. Ejemplo: Ordenación Strategy (SortStrategy) Define un interfaz común para todos los algoritmos soportados. Context utiliza esta interfaz para utilizar algún algoritmo definido por un ConcreteStrategy ConcreteStrategy (QuickSort, ShellSort, MergeSort) Implementa el algoritmo utilizando la interfaz Strategy. Context (SortedList) Está configurado con un objeto de ConcreteStrategy. Mantiene una referencia a un objeto Strategy. Puede definir un interfaz para permitir que Strategy acceda a sus datos. namespace StrategyPatterns { // Definición del interfaz public interface ISort { void Sort(List<string> list) } // Implementación QuickSort public class CQuickSorter : ISort { void Sort(List<string> list) { // Implementación Quick Sort } } // Implementación Burbuja public class CBurbujaSorter { void Sort(List<string> list) { // Implementación en Burbuja } } // Implementación MergeSort public class CMergeSort { void Sort(List<string> list) { // Implementación MergeSort } } public class Contexto { private ISort sorter; public Contexto(ISort sorter) { this.sorter = sorter; } public ISort Sorter { get{return sorter;) } } public class MainClass { static void Main() { List<string> myList = new List<string>(); myList.Add("Hola mundo"); myList.Add("Otro item"); myList.Add("Tercer item"); Contexto cn = new Contexto(new CQuickSorter()); cn.Sorter.Sort(myList); // Ordenado con quicksort myList.Add("Uno más para el merge sort"); cn = new Contexto(new CMergeSort()); cn.Sorter.Sort(myList); // Ordenado con mergesort } } } Ejemplo II: Coche Ejemplos reales 1. En .NET se aplica con bastante frecuencia. Uno de los ejemplos más concretos se corresponde con la infraestructura de acceso a base de datos en la cual existen una serie de interfaces (IDbConnection, IDBCommand...) que reciben una implementación concreta según la base de datos (estrategia) que se desee implementar (OracleConnection, OracleCommand...) 2. En los lectores de datos. El interfaz vuelve a estar bien definido, necesitamos un determinado flujo de datos (un stream de bytes, una cadena de texto claro, una cadena xml) pero el origen de ese determinado flujo puede variar. De esta forma existen diversos interfaces (IStreamReader, IXMLReader, ITextReader) así como una serie de implementadores de dichos interfaces, TCPReader, FileReader... etc. Visitor Nombre del patrón: Visitor - Visitante Clasificación del patrón: Patrones de comportamiento Problema: Queremos añadir un nuevo método a la jerarquía de clases, pero el hecho de hacerlo estropeará el diseño. Contexto: Implementación del sistema separando las funcionalidades heterogéneas de las clases, sin modificar la jerarquía de clases. Prioridades: Flexibilidad, para que la adición de nuevos elementos alteren el mínimo número de clases posible. Objetivos: Encapsular la operación que se realizará sobre los elementos de una estructura de datos. De esta forma podemos realizar cambios posteriores en la operación sin cambiar las clases de elementos sobre los que opera. Estructura Participantes: Visitante: Declara la interfaz de visitante con la operación Visitar para cada clase de ElementoConcreto en la estructura de objetos. El nombre de operación y su signatura identifican la clase que envía la petición Visitar al Visitante, permitiendo al Visitante determinar la clase concreta del elemento a visitar. El Visitante puede acceder al elemento directamente a través de su interfaz particular. VisitanteConcreto: Implementa cada operación declarada por el Visitante. Proporciona el contexto para el algoritmo y almacena el estado local (normalmente acumula los resultados durante el paso por la estructura). Cliente: Crea un objeto VisitanteConcreto y recorre la estructura de objetos, visitando cada elemento con el visitante. Elemento: Define la operación Acceptar que tiene un visitante como argumento. Elemento Concreto: Implementa la operación Aceptar. Estructura de Objetos: Enumeración de sus elementos. Proporciona una interfaz de alto nivel para que los visitantes visiten sus elementos. Pude ser una composición o colección (list, set). Colaboraciones: El cliente crea un objeto VisitanteConcreto y recorre la estructura de objetos, visitando cada elemento con el visitante. Cuando un elemento es visitado, hace la llamada al método del visitante que corresponde con su clase. El elemento se proporciona a si mismo como argumento para esta operación para dejar que el visitante acceda a su estado, si es necesario. Ventajas: Se pueden añadir nuevas operaciones es fácilmente. Se pueden agrupar operaciones relacionadas en una clase en lugar de modificar o derivar clases para ello. Colaborar con el patron Composite. Inconvenientes: Dificulta la adición de nodos (requiere la modificación de cada visitante existente), por lo que el conjunto de clases tiene que estar definido a priori. Cada clase tiene que tener un método de aceptación (accept method). Patrones relacionados: Composite: para aplicar una operación a una estructura de objetos. Interpreter: se puede aplicar el patrón visitante para la interpretación. Ejemplo: Consideremos este ejemplo de la interfaz de un modem. Contiene los métodos genéricos de los distintos modelos disponibles. Supongamos que queremos configurar los modems para Linux con un método nuevo.
© Copyright 2024