miércoles, 13 de febrero de 2013

Iterator Pattern

  • Clasificación del patrón: de comportamiento.
  • Intención: proporcionar una forma de acceder secuencialmente a los elementos de un objeto agregado, sin exponer su representación interna.
  • También conocido como: Cursor.
  • Aplicabilidad: Se utiliza este patrón cuando se necesita
    • para acceder al contenido de un objeto agregado sin exponer su representación interna.
    • para apoyar recorridos múltiples sobre objetos agregados.
    • para proporcionar una interfaz uniforme para atravesar diferentes estructuras agregadas (es decir, para apoyar iteración polimórfica).
  • Estructura:
  • Participantes
    • Iterator: define una interfaz para acceder y recorrer los elementos.
    • ConcreteIterator: implementa la interfaz Iterator y realiza un seguimiento de la posición actual en el recorrido del agregado.
    • Agregado: define una interfaz para crear un objeto Iterator.
    • ConcreteAggregate: implementa la interfaz de creación de Iterator para devolver una instancia de la ConcreteIterator adecuada.
  • Colaboraciones
    •  Un ConcreteIterator mantiene un seguimiento del objeto actual en el agregado y puede calcular el objeto siguiente en el recorrido.
  • Implementación
    • ¿Quién controla la iteración?
      • Si es el cliente, el iterador es un iterador externo. Estos son más flexibles que los internos. Es fácil comparar dos colecciones para con un repetidor externo, pero es prácticamente imposible para los internos.
      • Si es el iterador, es un iterador interno.
    • ¿Quién define el algoritmo de recorrido? 
      • El iterador: es fácil usar diferentes algoritmos de iteración en el mismo agregado, y también puede ser más fácil de reutilizar el mismo algoritmo sobre diferentes agregados. Pero esta solución posdía violar la encapsulación del agregado en caso de que el algoritmo de recorrido necesite acceder a las variables privadas del agregado.
      • El agregado puede definir el algoritmo de recorrido y utilizar el iterador para almacenar sólo el estado de la iteración. Llamamos a esta clase de iterador un "cursor", ya que se limita a señalar la posición actual en el agregado.
    • Un iterador robusto asegura que las inserciones y eliminaciones no interfiera con recorrido.
    • Operaciones adicionales: la interfaz Iterator tiene como base las operaciones first(), next(), isDone() y currentItem(), pero podría ser necesario agregar más. Por ejemplo, previous() o goTo().
    • Iteradores de composites: los iteradores externos pueden ser difícil de implementar sobre estructuras compuestas, ya que tiene que almacenar una ruta a través del Composite para no perder de vista el objeto actual. A veces es más fácil simplemente utilizar un iterador interno.

      Si los nodos de un Composite tiene una interfaz para pasar de un nodo a sus hermanos, padres e hijos, entonces un iterador "cursor" puede ofrecer una alternativa mejor.
    • NullIterator: es útil para el manejo de contornos, donde los elementos Aggregate devuelven un iterador concreto como de costumbre, pero los elementos hoja devuelven una instancia de NullIterator, el cual siempre evalúa IsDone () como true.
  • Consecuencias:  
    • Permite que los agregados puedan ser atravesados ​​de diferentes maneras. Los iteradores facilitan el cambio de algoritmo de recorrido: basta con sustituir la instancia del iterador con una diferente o instanciar una subclases Iterator, que puede realizar nuevos recorridos.
    •  Los iteradores simplifican la interfaz Aggregate.
    • Un iterador mantiene un registro de su propio estado transversal, por lo tanto puede tener más de un recorrido en curso a la vez.
  • Motivación: el patrón surge por la necesidad de acceder a los elementos de un contenedor de objetos (un Aggregate) sin exponer su representación interna. Además, es posible requerir de más de una forma de recorrer la estructura.

    La solución que propone el patrón es que la responsabilidad del recorrido se traslade a un objeto iterador, y añadirle métodos que permitan recorrer la estructura sin referenciar explícitamente su representación.
  • Ejemplo: 
  • Patrones relacionados
    • Composite: los Iterators son frecuentemente aplicados sobre composites.
    • Factory Method: iteradores polimórficos se basan en Factory Methods para crear una instancia de la subclase Iterator apropiado.
    • Memento: se utiliza a menudo en conjunto con el patrón Iterator. Un iterador puede usar un memento para capturar el estado de una iteración. El iterador guarda el memento internamente

martes, 12 de febrero de 2013

Template Method

  • Clasificación del patrón: de comportamiento.
  • Intención: definir el esqueleto de un algoritmo dentro de una operación, dejando que las subclases redefinan ciertos pasos del algoritmo, pero sin cambiar su estructura.
  • También conocido como: ---
  • Aplicabilidad: Se utiliza este patrón cuando se necesita
    • aplicar las partes invariables de un algoritmo una vez y dejar en manos de las subclases la implementación del comportamiento que puede variar.
    • evitar la replicación de código mediante generalización: se factoriza el comportamiento común de varias subclases en una única superclase.
    • controlar las extensiones de las subclases. El Método Plantilla utiliza métodos especiales (métodos de enganche o hooks) en ciertos puntos, siendo los únicos puntos que pueden ser redefinidos y, por tanto, los únicos puntos donde es posible la extensión.
  • Estructura:
  • Participantes
    • AbstractClass: 
      • define operaciones primitivas abstractas que luego las subclases concretas deberán definir para implementar los pasos de un algoritmo.
      • implementa un método plantilla que define el esqueleto de un algoritmo y que llama operaciones primitivas así como operaciones definidas en AbstractClass o en otros objetos.
    • ConcreteClass: implementa las operaciones primitivas para llevar a cabo pasos específicos del algoritmo.
  • Colaboraciones
    • ConcreteClass se basa en AbstractClass para implementar los pasos invariables del algoritmo.
  • Implementación
    • Es recomendable declarar las operaciones primitivas de tal forma que sólo puedan ser llamadas por el método plantilla (protected, por ejemplo).
    • Debe reducirse en la medida de lo posible el número de operaciones primitivas que van a ser invocadas desde el método plantilla. De este forma se reducirá la complejidad de las subclases y resultará menos tediosa su implementación.
    • Convenciones de nombres: puede identificar las operaciones a sobreescribir mediante la adición de un prefijo a sus nombres.
  • Consecuencias
    • La utilización de este patrón es fundamental a la hora de reutilizar código.
    • Se invierte el control: en este caso la superclase es la encargada de llamar a las operaciones definidas en las subclases.
    • Template method puede llamar a:
      • Operaciones concretas: de la clase concreta o de un cliente.
      • Operciones primitivas: implementadas en la superclase.
      • Operaciones hooks: proporcionan código por defecto que puede ser refinado en las subclases si es necesario. A veces, por defecto, no hacen nada.
      • factory methods.
    • Muy útil en: 
      • implementación de bibliotecas de clases, ya que permiten extraer el comportamiento común de las clases de la biblioteca. 
      • sistemas de plugins, gracias principalmente a la utilización de las operaciones hooks.
  • Motivación: existen casos en que se necesita que varias subclases tengan un comportamiento similar, pero con algunas modificaciones en algunos de sus casos. Mediante la definición de algunos de los pasos de un algoritmo utilizando operaciones abstractas, el método de la plantilla fija su orden, y  permite que las subclases varíen los pasos que se adaptan a sus necesidades.
  • Ejemplo: 
  • Patrones relacionados
    • Factory Method: son llamados a menudo por un TemplateMethod.
    • Strategy: utilizan la delegación para variar el algoritmo entero, no solo una parte como lo hace el TemplateMethod.

Decorator Pattern

  • Clasificación del patrón: estructural.
  • Intención: extender la funcionalidad de un objeto en forma dinámica,  proporcionando una alternativa flexible a la creación de subclases.
  • También conocido como: Wrapper.
  • Aplicabilidad: Se utiliza este patrón cuando
    • se necesita añadir responsabilidades a objetos individuales dinámica y transparente, es decir, sin afectar a otros objetos.
    • se necesita retirar responsabilidades de algunos objetos.
    • la extensión por subclases es poco práctica. Por ejemplo, un gran número de extensiones independientes son posibles y produciría una explosión de subclases por cada combinación. También cuando la definición de clase puede estar oculta o no disponible para subclases.
  • Estructura:
  • Participantes
    • Component: define una interfaz para los objetos a externder en forma dinámica.
    • ConcreteComponent: define un objeto al cual se le puede extender con responsabilidades adicionales.
    • Decorator: mantiene una referencia a un objeto componente y define una interfaz que se ajusta a la interfaz de componente.
    • ConcreteDecorator: agrega responsabilidades al componente.
  • Colaboraciones
    • ConcreteDecorator-Component: el primero reenvía solicitudes a su objeto Component. Opcionalmente puede realizar operaciones adicionales antes y después de remitir la solicitud.
  • Implementación
    • La interfaz de un objeto decorador debe ajustarse a la interfaz del componente que decora (por herencia).
    • Omitiendo la clase Decorador abstracta. No hay necesidad de definir una clase Decorador abstracta cuando sólo tiene que añadir una responsabilidad.
    • Componentes livianos. Es importante mantener la clase Component liviana para evitar que los decoradores resulten demasiado cargados, es decir, debe centrarse en la definición de una interfaz, no en el almacenamiento de datos (quienes deberían ser tratados en subclases). Además, esta interfaz debe ser limitada, ya que si es amplia aumenta la probabilidad de que las subclases concretas terminen heredando funciones que no necesitan.
    • Decoratos vs Strategy: son dos formas alternativas de cambiar un objeto. El patrón Decorador sólo cambia un componente desde el exterior, de modo que los decoradores son transparentes para el componente. Con Strategy, el componente conoce las posibles extensiones, teniendo que hacer referencia y mantener las correspondientes estrategias.
  • Consecuencias
    • Ventajas
      • Mayor flexibilidad que la herencia estática. Con decoradores, las responsabilidades se pueden agregar y quitar en tiempo de ejecución con sólo conectar y desconectar ellos. Por el contrario, la herencia requiere la creación de una nueva clase para cada responsabilidad adicional.
      • Evita las clases cargadas de características, ya que permite la adición de responsabilidades. En lugar de tratar de soportar todas las características previsibles de una clase compleja y personalizable, se puede definir una clase simple y agregar funcionalidad incremental con objetos reenvía.
      • Son fáciles de personalizar por aquellos que entienden su estructura.
    • Desventajas
      • Un decorador y su componente no son idénticos. Un decorador actúa como una caja transparente, pero desde el punto de vista de la identidad del objeto un componente decorado no es idéntico a la del propio componente.
      • Un diseño que utiliza Decorator a menudo resulta en sistemas compuestos por gran cantidad de pequeños objetos parecidos. Los objetos sólo se diferencian en la manera en que están interconectados. Esto puede resultar difícil de aprender y depurar.
  • Motivación: A veces es necesario añadir responsabilidades a objetos individuales, no a una clase entera. Por ejemplo, cuando un conjunto de herramientas de interfaz gráfica de usuario debe permitir agregar propiedades a cualquier componente. Una forma de añadir responsabilidades es con la herencia, pero es poco flexible.
  • Ejemplo:
  • Patrones relacionados
    • Adapter: un Decorator sólo cambia las responsabilidades de un objeto, mientras que un adaptador dará a un objeto una interfaz nueva.
    • Composite: un Decorator puede ser visto como un composite de un solo componente. Sin embargo, un decorador añade responsabilidades adicionales.
    • Strategy: un Decorator permite cambiar la "piel" de un objeto, mientras que un Strategy le permite cambiar las "tripas" (por dentro).

lunes, 11 de febrero de 2013

Observer Pattern

  • Clasificación del patrón: de comportamiento.
  • Intención: definir una dependencia de uno-a-muchos con otros objetos, de forma que cuando un objeto cambia de estado todos sus dependientes son notificados y actualizados automáticamente.
  • También conocido como: Dependents, Publish-Subscribe.
  • Aplicabilidad: Se utiliza este patrón cuando se necesita
    • Cuando una abstracción tiene dos aspectos dependientes. Encapsular estos aspectos en objetos separados le permite variar y volver a utilizarlos de forma independiente.
    • Cuando un cambio en un objeto requiere cambiar a los demás, y no se sabe cuántos objetos necesitan ser cambiados.
    • Cuando un objeto debe ser capaz de notificar a otros objetos sin hacer suposiciones acerca de quién son estos objetos. En otras palabras, usted no quiere que estos objetos estrechamente acoplados.
  • Estructura:
  • Participantes
    • Subject: conoce a sus observadores. Cualquier número de objetos observador puede observar un sujeto. proporciona una interfaz para acoplar y desacoplar los objetos del observador.
    • Observador: define una interfaz de actualización para los objetos que deben ser notificados de los cambios en un sujeto.
    • ConcreteSubject: 
      • almacena su estado para los objetos ConcreteObserver
      • envía una notificación a sus observadores cuando su estado cambia.
    • ConcreteObserver: 
      • mantiene una referencia a un objeto de ConcreteSubject.
      • es el encargado de asignar un estado al ConcreteSubject. 
      • implementa la interface de actualización del Observer.
  • Colaboraciones
    • ConcreteSubject - ConcreteObservers: el primero notifica a sus observadores cada vez que ocurre un cambio en su estado, para que los observadores efectúen las operaciones correspondientes al nuevo estado, pudiendo consultar al Subject para conciliar su estado con el del sujeto.
  • Implementación
    • Asignación de sujetos a sus observadores. 
      • almacenar referencias a ellos explícitamente en el Subject. 
      • cuando hay muchos sujetos y pocos observadores, se puede negociar el espacio mediante el uso de una búsqueda asociativa (ej: una tabla hash) para mantener la asignación de sujeto a observador. Así, un sujeto sin observadores no incurre en gastos generales de almacenamiento, aunque aumenta el coste de acceso a los observadores.
    • Observando más de un Subject: podría tener sentido cuando un observador dependa de más de un sujeto, como podría ser el caso de una hoja de cálculo que dependa de más de una fuente de datos. El observador debe tener otra interfaz de actualización, para saber cuál de los dos lo invocó, o puede pasarsele el Subject por parámetro.
    • ¿Quién debe llamar a notify()? 
      • Tras cada cambio en Subject se llama a notify(). Esto libera a los clientes de tener que llamar a notify, pero causa una gran cantidad de Updates por cada pequeño cambio.
      • Que los clientes sean responsables de llamarlo. Esto permite llamar al método después de una serie de cambios sucesivos y evitar repetidas actualizaciones, pero la desventaja es que el cliente puede olvidar llamar a notify().
    • Cuando se elimina un Subject se debe avisar a los observadores de ello, para que eliminen su referencia a él. Borrar los observadores no es una buena solución, ya que pueden estar observando a otros Subject u Observers.
    • Asegurarse de que Subject sea consistente antes de llamar a notify().
    • Evitar update() específicos: 
      • Push model: el sujeto envía información detallada acerca del cambio a los observadores. Esto es ineficiente porque algunos observadores pueden no necesitar cierta información pasada
      • Pull model: el sujeto envía una notificación y los observadores deben pedir los detalles explícitamente al Subject. Esto es eficiente porque cada observer pide lo que necesita, pero ineficiente porque es difícil deducir qué cambió en el subject.
    • Hacer update() para observadores específicos. Se puede registrar los observadores sólo para eventos específicos. Cuando ocurre un evento, el sujeto informa sólo aquellos observadores que han registrado interés en ese evento.
    • ChangeManager para una actualización compleja: cuando el Subject y el Observer es compleja, se puede agregar un objeto intermediario. 
      • Vincula un sujeto con sus observadores.
      • Define una estrategia de actualización particular.
      • Actualiza todos los observadores dependientes cuando un sujeto lo requiere.
    • Combinación del Sujeto y los Observers: en lenguajes que carecen de herencia múltiple, como Smalltalk, generalmente no definen el Subject  y los Observers por separado, sino que combinan sus interfaces en una clase.
  • Consecuencias
    • El acoplamiento entre sujeto y observadores es abstracto y mínimo, por lo que los cambios en un sujeto o un observador no afectan al otro y se puede añadir nuevos observadores en cualquier momento.
    • Soporte para broadcast. Esto permite al Subject agregar y quitar observadores en cualquier momento, ya que todo depende del observador para manejar o hacer caso omiso de la notificación. 
    • Actualizaciones inesperadas, que provocan una cascada de cambios a los observadores y sus objetos dependientes. Además, el método update() no proporciona detalles sobre lo que ha cambiado en el Subject, de modo que los observadores pueden verse obligados a trabajar duro para deducir los cambios.
  • Motivación: Al dividir un sistema en un conjunto de clases cooperantes surge la necesidad de mantener coherencia entre los objetos relacionados, pero sin que estén estrechamente vinculados, porque eso reduce su reutilización.
  • Ejemplo:
  • Patrones relacionados
    • Mediator: el ChangeManager actúa como mediador entre los sujetos y observadores.
    • Singleton: el ChangeManager puede usar el patrón Singleton para ser único y accesible globalmente.

sábado, 9 de febrero de 2013

Adapter Pattern

  • Clasificación del patrón: estructural.
  • Intención: transformar la interfaz de un objeto existente en otra que los clientes puedan utilizar. De este modo, una clase que no puede utilizar la primera, puede hacer uso de ella a través de la segunda.
  • También conocido como: Wrapper.
  • Aplicabilidad: Se utiliza este patrón cuando se necesita
    • utilizar una clase existente, pero su interfaz no coincide con la que se necesita.
    • crear una clase reutilizable que coopera con clases que no tienen interfaces compatibles. 
    • utilizar varias subclases existentes, pero como es poco práctico adaptar cada interfaz, se crea un objeto que adapte la interfaz de la clase padre.
  • Estructura:
Object Adapter: utiliza composición

Class Adapter: utiliza herencia múltiple
  • Participantes
    • Target: define la interfaz específica que el cliente utiliza.
    • Client: interactúa con los objetos según la interfaz de Target.
    • Adaptee: define una interfaz existente que necesita ser adaptada.
    • Adapter: adapta la interfaz de Adaptee a la interfaz de destino.
  • Colaboraciones
    • Client-Adapter: el cliente llama a las operaciones de una instancia de Adapter.
    • Adapter-Adaptee: el adaptador llama a las operaciones de Adaptee, quienes realizan la petición.
  • Implementación
    • Crear una clase Adapter que hereda públicamente de Target y tiene una instancia de Adaptee o hereda en forma privada de este.
    • Pluggable Adapters:
      1. encontrar una interfaz general para Adaptee (Adapter), que tenga un pequeño conjunto de operaciones que permitan hacer la adaptación.
      2. alguna de estas opciones:
        1. Uso de operaciones abstractas: se definen operaciones abstractas en el cliente y el Adapter debe implementarlas  y adaptar el objeto Adaptee.
        2. Uso de objetos delegados mediante Strategy: el cliente puede utilizar un Strategy para asignarse un tipo de AdapterStrategy.
        3. Adaptadores parametrizados:  el adaptador almacena un bloque para cada solicitud individual. No soporta adaptación de subclases.
  • Consecuencias
    • Una clase adaptador:
      • Al tratarse de herencia múltiple sólo puede heredar de un adaptee concreto y no funcionará cuando queremos adaptar una clase y sus subclases.
      • No tiene que implementar un Adaptee por completo. Sólo aquellas partes que necesita. También puede reemplazar el comportamiento del adaptee.
      • al heredar del Adaptee no es necesario un puntero para acceder al Adaptee.
    • Un objeto adaptador:
      • puede tener uno o muchos Adaptees (el adaptee y sus subclases, si posee). También puede agregar funcionalidad a todos los Adaptees a la vez.
      • Es más flexible, ya que escribe poco código y delega al Adaptee. 
      •  Como se trata de manera general el comportamiento de todos los Adaptee, si una subclase agrega nuevas funcionalidades no se puede adaptar directamente. En ese caso se podría tener un adaptador por cada tipo de subclase.
  • Motivación: a veces, una clase no se puede reutilizar sólo porque su interfaz no coincide con la interfaz que una aplicación requiere. 
  • Ejemplos:
Dibujando un TextView en lugar de una figura

  • Consecuencias
    • Una clase adaptador:
      • Al tratarse de herencia múltiple sólo puede heredar de un adaptee concreto y no funcionará cuando queremos adaptar una clase y sus subclases.
      • No tiene que implementar un Adaptee por completo. Sólo aquellas partes que necesita. También puede reemplazar el comportamiento del adaptee.
      • al heredar del Adaptee no es necesario un puntero para acceder al Adaptee.
    • Un objeto adaptador:
      • puede tener uno o muchos Adaptees (el adaptee y sus subclases, si posee). También puede agregar funcionalidad a todos los Adaptees a la vez.
      • Es más flexible, ya que escribe poco código y delega al Adaptee. 
      •  Como se trata de manera general el comportamiento de todos los Adaptee, si una subclase agrega nuevas funcionalidades no se puede adaptar directamente. En ese caso se podría tener un adaptador por cada tipo de subclase.
 

Haciendo pasar pavo por pato


  • Preguntas
    • ¿Cuánto se debe adaptar? La cantidad de métodos a adaptar es proporcional a la interface que se debe soportar como Target.
    • ¿El adaptador solo wrappea una clase? No, puede haber casos en que wrapee más de un Adaptee.
    • Mi sistema debe trabajar con dos interfaces: una nueva y una vieja ¿Qué se puede hacer?. Crear un Two Way Adapter que implemente ambas interfaces implicados, por lo que el adaptador puede actuar como una interfaz vieja o nueva. Se puede utilizar herencia múltiple, porque las dos interfaces son substancialmete diferentes.
  • Patrones relacionados
    • Bridge: separa una interfaz de su aplicación, de manera que pueda variar fácilmente y de forma independiente.
    • Decorator: no altera la interfaz, pero agrega responsabilidad.
    • Proxy: define un representante o sustituto de otro objeto sin cambiar su interfaz.
    • Facade:  hace que una interfaz sea más simple.

Facade Pattern

  • Clasificación del patrón: estructural.
  • Intención: proporcionar una interfaz unificada para un conjunto de interfaces de un sistema, y hace que los subsistemas sean mas facil de usar.
  • También conocido como: ---
  • Problema: Se utiliza este patrón cuando se necesita
    • minimizar la comunicación y las dependencias entre subsistemas.
    • proporcionar una interfaz sencilla para un subsistema complejo.
    • eliminar dependencias entre clientes y clases de implementación, porque son demasiadas.
    • dividir conjuntos de subsistemas en capas.
  • Solución:  desacoplar el subsistema de los clientes y otros subsistemas, promoviendo así la independencia y la portabilidad.
  • Estructura:
  • Participantes
    • Facade: Conoce cuales clases del subsistema son responsables de una peticion y delega las peticiones de los clientes en los objetos del subsistema.
    • SubsystemClasses: implementan su propia funcionalidad y manejan el trabajo asignado por el objeto Facade. No saben de la existencia del Facade.
  • Colaboraciones
    • Client-Subsystem: Los clientes se comunican con el subsistema enviando peticiones a la fachada, quien los reenvía al subsistema apropiado. El Facade puede tener que hacer un trabajo propio para traducir su interfaz a las interfaces del subsistema.
    • El objetivo es evitar que los clientes que utilizan la fachada puedan acceder a los subsistemas directamente (aunque podría implementarse un Facade opcional).
  • Consecuencias
    • el Facade protege a los clientes de los componentes del subsistema.
    • permite modificar los componentes del subsistema sin afectar a sus clientes. 
    • no impide que las aplicaciones utilicen los subsistemas si es necesario.
  • Implementación
    • Reducir el acoplamiento del cliente y los subsistemas. Para ello, el Facade podría ser una clase abstracta.
    • Se puede elegir entre mantener públicos a los subsistemas, de modo que sean accesibles tanto directamente como mediante el Facade, o convertirlos en privados y sólo dejar que el Facade les delegue responsabilidades. Esto último podría provocar que el cliente termine utilizando muchas menos funciones o se encuentre limitado a las acciones que puede realizar sólo mediante el Facade.
  • Motivación
  • Patrones relacionados
    • Un Facade es a menudo un Singleton.
    • Se puede combinar e incluso puede reemplazar al AbstractFactory.
    • Es muy parecido al Mediator Pattern, ya que ambos abstraen la funcionalidad de clases existentes. La diferencia se encuentra en que el Facade no define nueva funcionalidad y los subsistemas no conocen al Facade, como si lo puede hacer un Mediator.

jueves, 7 de febrero de 2013

Abstract Factory Pattern

  • Clasificación del patrón: creacional.
  • Intención: proporcionar una interfaz para crear familias de objetos relacionados o dependientes, sin especificar sus clases concretas.
  • También conocido como: Kit.
  • Problema: Se utiliza este patrón cuando se necesita
    • crear una familia de objetos relacionados, diseñada para ser utilizada en conjunto.
    •  que un sistema sea independiente de la forma en que sus productos son creados, compuestos o representados. 
    • que un sistema sea configurado con una de múltiples familias de productos. 
    • proporcionar una biblioteca de clases de productos, y sólo se quiere revelar sus interfaces, no sus implementaciones.
  • Solución: que el cliente delegue en otro objeto la creación de familias de objetos.
  • Estructura:
  • Participantes
    • AbstractFactory: declara una interfaz para la creación de productos abstractos. 
    • ConcreteFactory: implementa las operaciones para crear productos en concreto.
    • AbstractProduct: declara una interfaz para un tipo de producot en concreto.
    • Product: define un producto que será creado por un ConcreteFactory e implementa la interfaz AbstractProduct. 
    • Client: usa la interfaz declarada por el AbstractFactory y AbstractProduct.
  • Colaboraciones
    • Cliente-ConcreteFactory: generalmente  se crea una sola instancia de ConcreteFactory en tiempo de ejecución, que crea productos particulares. Para crear objetos de diferente tipo de producto, los clientes deben utilizar una ConcreteFactory diferente.
    • AbstractFactory aplaza la creación de objetos de productos para su subclase ConcreteFactory.
  • Consecuencias
    • Ventajas:
      • Aísla las clases concretas.
      • Facilita el intercambio de familias de productos.
      • Promueve la consistencia entre productos: un cliente utiliza sólo una familia de productos a la vez.
    • Desventaja: 
      • Es difícil dar cabida a nuevos tipos de productos, ya que se debe modificar la clase abstracta y todas las subclases para soportarlo.
  • Implementación
    • Las ConcreteFactories suelen implementarse como Singletons.
    • Si deben crearse muchas familias de productos, se puede aplicar el Prototype Pattern.
    • Las AbstractFactories definen un número finito de operciones creadoras de productos. Si se quiere agregar un producto de esta manera, es difícil, porque hay que cambiar la interfaz y todas las subclases. Una posible solución es tener un solo método y llamarlo para cada producto a crear.
  • Patrones relacionados
    • Las AbstractFactory se implementan a menudo con Factory Method, pero también puede ser implementado usando Prototype
    • Una ConcreteFactory es a menudo un Singleton.
  • Motivación