Patrones de Diseño de Software GoF: Definiciones, Participantes y Consecuencias

Clasificado en Informática

Escrito el en español con un tamaño de 18,6 KB

1. Patrón Strategy (Estrategia)

Definición: Define una familia de algoritmos, encapsula a cada uno de ellos y los hace intercambiables. Permite que el algoritmo varíe de forma independiente respecto a los clientes que lo utilizan.

Aplicabilidad

  • Clases con diferentes comportamientos.
  • Distintas variantes de un mismo algoritmo.
  • Un algoritmo utiliza datos que los clientes no deben conocer.
  • Presencia de sentencias condicionales con múltiples ramas complejas.

Participantes

  • Strategy (Estrategia): Declara una interfaz común para todos los algoritmos admitidos. El contexto la utiliza para llamar al algoritmo.
  • ConcreteStrategy / CS (Estrategia Concreta): Implementa los distintos algoritmos utilizando la interfaz Strategy.
  • Context / CTX (Contexto): Se configura con un objeto ConcreteStrategy específico; mantiene una referencia a un objeto Strategy y puede definir una operación que permita al objeto Strategy acceder a sus datos.
  • Cliente: Solicita la acción y configura la estrategia concreta. No contiene la lógica, solo interactúa con el Contexto.

Consecuencias

  • Familia de algoritmos relacionados: La jerarquía de la clase Strategy define una familia de algoritmos reutilizables.
  • Alternativa a la herencia: Se podría heredar de la clase Contexto, pero esto ligaría el comportamiento mezclando la implementación del algoritmo con el contexto, impidiendo modificarlo dinámicamente y provocando una explosión de subclases.
  • Elimina sentencias condicionales: Encapsula los comportamientos en clases Strategy separadas, relegando las sentencias condicionales únicamente a la selección o creación de objetos.
  • Respeta los principios OCP y SRP: Tanto Strategy como Context tienen una única razón de cambio.
  • Inconvenientes: Los clientes deben conocer las diferencias entre las diversas estrategias concretas para elegir la correcta. Puede haber sobrecarga de comunicación entre Context y Strategy (algunas implementaciones concretas no necesitan todos los datos recibidos de la interfaz Strategy). Además, incrementa el número global de objetos en el sistema.

2. Patrón Factory Method (Método de Fábrica)

Definición: Define una interfaz para crear un objeto, pero delega en las subclases la decisión sobre qué clase instanciar. Permite que una clase delegue la creación de instancias a sus subclases. El método de fábrica suele ser abstracto y las subclases deben redefinirlo.

Aplicabilidad

  • Una clase no puede prever de antemano la clase de objetos que debe crear.
  • Una clase quiere que sean sus subclases las que especifiquen los objetos a crear.

Participantes

  • Product (Producto): Define la interfaz de los objetos que crea el método de fábrica.
  • ConcreteProduct / CP (Producto Concreto): Implementa la interfaz de Product.
  • Creator (Creador): Declara el método de fábrica que devuelve un objeto de tipo Product. Puede definir una implementación por defecto que devuelva un ConcreteProduct predeterminado y llamar al método de fábrica para operar con dicho producto.
  • ConcreteCreator / CC (Creador Concreto): Redefine el método de fábrica para devolver una instancia específica de un ConcreteProduct.

Consecuencias

  • El código cliente solo trata con la interfaz Product, por lo que puede funcionar con cualquier clase ConcreteProduct definida por el usuario.
  • Inconvenientes: Puede requerir crear una subclase de Creator únicamente para instanciar un ConcreteProduct concreto.
  • Crear objetos dentro de una clase mediante este patrón otorga una flexibilidad notablemente mayor que hacerlo mediante instanciación directa.

3. Patrón Abstract Factory (Fábrica Abstracta)

Definición: Proporciona una interfaz para crear familias de objetos relacionados o que dependen entre sí, sin especificar sus clases concretas.

Aplicabilidad

  • Un sistema debe ser independiente de cómo se crean, componen y representan sus productos.
  • Un sistema debe configurarse con una familia de productos concreta elegida entre varias, y dicha familia está diseñada para ser utilizada de forma conjunta.
  • Se desea proporcionar una biblioteca de clases de productos revelando solo sus interfaces, no sus implementaciones.

Participantes

  • AbstractFactory / FA (Fábrica Abstracta): Declara una interfaz para las operaciones que crean objetos de productos abstractos.
  • ConcreteFactory / FC (Fábrica Concreta): Implementa las operaciones para crear objetos de productos concretos.
  • AbstractProduct / PA (Producto Abstracto): Declara una interfaz para un tipo de objeto de producto.
  • ConcreteProduct / PC (Producto Concreto): Define un objeto de producto para ser creado por la fábrica concreta correspondiente e implementa la interfaz de AbstractProduct.
  • Cliente: Solo utiliza las interfaces declaradas por AbstractFactory y AbstractProduct.

Consecuencias

  • Aísla las clases concretas: Ayuda a controlar las clases de objetos que crea la aplicación. Encapsula la responsabilidad y creación de los objetos producto y aísla a los clientes de las implementaciones, manipulándolos a través de interfaces abstractas.
  • Facilita el intercambio de familias de productos: La clase de una fábrica concreta solo aparece una vez en la aplicación (en su instanciación), lo que facilita cambiar la fábrica completa que se está utilizando.
  • Promueve la consistencia entre productos: Facilita el cumplimiento de la restricción de que una familia completa de productos trabaje siempre de forma conjunta.
  • Desventajas: Es difícil dar cabida a nuevos tipos de productos. Añadir un producto nuevo exige modificar la clase AbstractFactory y todas sus subclases de fábricas concretas, ya que la interfaz fija el conjunto de productos que se pueden crear.

4. Patrón Adapter (Adaptador)

Definición: Convierte la interfaz de una clase en otra interfaz que es la que esperan los clientes. Permite que cooperen clases que de otra forma no podrían hacerlo debido a interfaces incompatibles. Los clientes invocan operaciones sobre la instancia del Adaptador, y este a su vez invoca operaciones sobre el Adaptable para satisfacer la petición.

Aplicabilidad

  • Se desea usar una clase existente cuya interfaz no concuerda con la que se necesita.
  • Se desea crear una clase reutilizable que coopere con clases no relacionadas o no previstas, las cuales no tienen por qué compartir interfaces compatibles.
  • (Adaptador de objetos): Se necesita usar varias subclases existentes pero no resulta práctico adaptar su interfaz heredando de cada una de ellas de forma individual.

Participantes

  • Target / Obj (Objetivo): Define la interfaz específica del dominio que el Cliente utiliza.
  • Cliente: Clase que interactúa y utiliza el Adaptador a través de la interfaz Target.
  • Adaptee / Adaptable: Define la interfaz incompatible existente que necesita ser adaptada.
  • Adapter / Adaptador: Adapta la interfaz del Adaptable a la interfaz Target.

Consecuencias

Adaptador de Clases

  • Adapta una clase Adaptable concreta a Target; no sirve si se requiere adaptar una clase y todas sus subclases simultáneamente.
  • Permite que el Adaptador redefina parte del comportamiento del Adaptable al ser una subclase directa de este.
  • Introduce un solo objeto en el sistema.

Adaptador de Objetos

  • Permite que un mismo Adaptador funcione con muchos Adaptables (con la clase base y con todas sus subclases). También permite añadir funcionalidad a todos los Adaptables a la vez.
  • Hace más difícil redefinir el comportamiento interno del Adaptable.

5. Patrón Composite (Objeto Compuesto)

Definición: Compone objetos en estructuras de árbol para representar jerarquías de parte-todo. Permite que los clientes traten de manera uniforme tanto a los objetos individuales como a las composiciones de objetos. Si el receptor es una hoja, la petición se ejecuta directamente; si es un compuesto, este redirige las peticiones a sus componentes hijos, pudiendo realizar operaciones adicionales.

Aplicabilidad

  • Se requiere representar jerarquías de tipo parte-todo entre objetos.
  • Se busca que los clientes sean capaces de obviar las diferencias entre composiciones de objetos y objetos individuales, tratándolos a todos de manera homogénea.

Participantes

  • Component (Componente): Declara la interfaz para los objetos de la composición. Implementa el comportamiento predeterminado común a todas las clases y declara una interfaz para acceder y gestionar a sus componentes hijos.
  • Leaf (Hoja): Representa los objetos hoja en la composición (no tienen hijos) e implementa sus operaciones base.
  • Composite (Compuesto): Define el comportamiento de los componentes que tienen hijos, almacena dichos hijos e implementa las operaciones de la interfaz Component relacionadas con la gestión de hijos.
  • Cliente: Manipula los objetos de la estructura a través de la interfaz Component.

Consecuencias

  • Define jerarquías de clases compuestas por objetos primitivos y objetos complejos. Los primitivos pueden ensamblarse en compuestos y ser tratados de manera uniforme.
  • Simplifica el código del cliente, ya que este trata de idéntica forma a las estructuras compuestas y a los objetos individuales.
  • Facilita añadir nuevos tipos de componentes: se pueden incorporar nuevas subclases de Composite o Leaf sin necesidad de modificar el código de los clientes.
  • Desventajas: Puede hacer que el diseño sea excesivamente genérico. Resulta más difícil restringir qué componentes pueden pertenecer a un compuesto; no se puede delegar dicha verificación enteramente en el sistema de tipos en tiempo de compilación y se debe validar en tiempo de ejecución.

6. Patrón Decorator (Decorador)

Definición: Añade responsabilidades adicionales a un objeto de forma dinámica, proporcionando una alternativa flexible a la herencia para extender funcionalidades. Permite incorporar comportamientos de forma transparente para los clientes, de modo que estos no diferencian entre un componente decorado y uno sin decorar.

Aplicabilidad

  • Se desea añadir responsabilidades a objetos individuales de forma dinámica y transparente, sin afectar a otros objetos.
  • Cuando la extensión mediante herencia no es viable o daría lugar a una explosión de subclases debido a todas las combinaciones posibles.

Participantes

  • Component (Componente): Define la interfaz para los objetos a los que se les pueden añadir responsabilidades dinámicamente.
  • ConcreteComponent / CC (Componente Concreto): Define un objeto base al cual se le pueden añadir responsabilidades adicionales.
  • Decorator (Decorador): Mantiene una referencia a un objeto Component y define una interfaz conforme con la interfaz de Component.
  • ConcreteDecorator / DC (Decorador Concreto): Añade responsabilidades específicas al componente que envuelve.

Consecuencias

  • Ofrece más flexibilidad que la herencia estática. Permite añadir y eliminar responsabilidades en tiempo de ejecución, evitando la explosión de clases por combinatoria. Además, posibilita aplicar una misma propiedad o comportamiento varias veces.
  • Evita clases sobrecargadas de funciones en la parte superior de la jerarquía. Permite definir clases base ligeras y añadir funcionalidad progresivamente mediante decoradores.
  • Desventajas: Produce sistemas con muchos objetos pequeños similares que difieren únicamente en la forma en que están interconectados, lo cual puede complicar el aprendizaje y la depuración. Además, un decorador y su componente interno no son idénticos a nivel de identidad de objeto.

7. Patrón State (Estado)

Definición: Permite que un objeto modifique su comportamiento cada vez que cambia su estado interno. El objeto parecerá haber cambiado de clase.

Aplicabilidad

  • El comportamiento de un objeto depende de su estado interno, y este debe cambiar en tiempo de ejecución en función de dicho estado.
  • Las operaciones contienen largas estructuras condicionales con múltiples ramas dependientes del estado. State aísla cada rama en una clase independiente, tratando el estado como un objeto en sí mismo y respetando el principio de responsabilidad única (SRP).

Participantes

  • Context (Contexto): Define la interfaz de interés para los clientes y mantiene una instancia de una subclase de ConcreteState que define el estado actual.
  • State (Estado): Define una interfaz para encapsular el comportamiento asociado con un determinado estado del Contexto.
  • ConcreteState / EC (Estado Concreto): Cada una de las subclases que implementa el comportamiento asociado con un estado del Contexto.

Colaboraciones

  • El Contexto puede pasarse a sí mismo como parámetro a los objetos de estado que gestionan la transición.
  • El Contexto delega las peticiones dependientes del estado en el objeto ConcreteState actual.
  • El Contexto es la interfaz principal para los clientes; una vez configurado, los clientes no tratan directamente con los objetos State.

Consecuencias

  • Localiza el comportamiento específico de cada estado y lo divide en clases separadas, evitando la dispersión de estructuras condicionales a costa de incrementar el número total de clases.
  • Hace explícitas las transiciones de estado al utilizar objetos dedicados en lugar de variables o valores numéricos discretos.
  • Los objetos de estado se pueden compartir si no tienen variables de instancia propias (estados sin estado propio), permitiendo que múltiples contextos compartan el mismo objeto.
  • Gestión de acciones de entrada y salida: al cambiar de estado (por ejemplo, en un método setState) se pueden invocar automáticamente métodos específicos de entrada (entry) o salida (exit) asociados a cada estado.

8. Patrón Template Method (Método Plantilla)

Definición: Define el esqueleto de un algoritmo dentro de una operación, difiriendo algunos pasos a las subclases. Permite que las subclases redefinan ciertos pasos de un algoritmo sin cambiar su estructura global.

Aplicabilidad

  • Para implementar las partes invariantes de un algoritmo una sola vez y dejar que las subclases implementen las partes variables.
  • Para factorizar código común en una clase base compartida y evitar la duplicación de código.
  • Para controlar de forma estricta el modo en que las subclases extienden la lógica de la clase base.

Participantes

  • AbstractClass / AC (Clase Abstracta): Define operaciones primitivas abstractas que las subclases redefinen, e implementa el método plantilla que articula el esqueleto del algoritmo.
  • ConcreteClass / CC (Clase Concreta): Implementa las operaciones primitivas requeridas para completar los pasos del algoritmo.

Consecuencias

  • Facilita la creación de implementaciones concretas de un mismo algoritmo.
  • Fomenta un código más limpio al evitar la duplicidad mediante la reutilización en la clase base.
  • Estructura el algoritmo dividiéndolo en métodos y operaciones más pequeñas, facilitando el mantenimiento y las pruebas unitarias.

9. Patrón Command (Comando / Orden)

Definición: Encapsula una petición en forma de objeto, permitiendo parametrizar a los clientes con diferentes peticiones, encolar peticiones, mantener registros de operaciones y soportar operaciones que se pueden deshacer.

Aplicabilidad

  • Parametrizar objetos con una acción determinada a ejecutar.
  • Especificar, encolar y ejecutar peticiones en diferentes momentos temporales.
  • Soportar funcionalidad de deshacer (Undo). La interfaz debe incluir una operación para anular los efectos de una ejecución anterior, almacenando los comandos en un historial que se pueda recorrer en orden inverso.
  • Registrar cambios para reanudarlos o aplicarlos nuevamente en caso de caída del sistema (mediante operaciones de carga y almacenamiento).

Participantes

  • Command / Orden: Declara una interfaz común para la ejecución de una operación.
  • ConcreteCommand / OC (Comando Concreto): Define el enlace entre un objeto Receptor y una acción específica, e implementa la ejecución invocando las operaciones correspondientes del Receptor.
  • Client / CLI (Cliente): Crea una instancia de ConcreteCommand y establece su Receptor.
  • Invoker / INV (Invocador): Solicita a la orden que lleve a cabo la petición. Contiene la referencia al Command que ejecutará la acción.
  • Receiver / Receptor: Sabe cómo llevar a cabo las operaciones asociadas para satisfacer la petición. Cualquier clase puede actuar como receptor (en ocasiones el propio comando realiza la acción sin delegar en un receptor externo).

Consecuencias

  • Desacopla de manera estricta el objeto que invoca la operación del objeto que sabe cómo ejecutarla.
  • Los comandos son objetos de primer orden: se pueden manipular, almacenar y extender como cualquier otra entidad.
  • Permite ensamblar múltiples órdenes en una orden compuesta (macros mediante el patrón Composite).
  • Facilita la incorporación de nuevas órdenes al sistema sin alterar las clases existentes.

Entradas relacionadas: