Principios GRASP y Patrones GoF: Diseño de Software Orientado a Objetos

Principios GRASP: Asignación de Responsabilidades en Diseño Orientado a Objetos

1. Experto en Información

Pregunta

¿Cuál es un principio general del diseño de objetos y la asignación de responsabilidades?

Respuesta

Asignar responsabilidades al Experto en Información, es decir, a la clase que posee la información necesaria para llevar a cabo una tarea.

Consideraciones

  • Encapsulamiento de la información y, por ende, bajo acoplamiento.
  • Comportamiento distribuido entre las clases, lo que resulta en clases más cohesivas.

Ejemplo

En una aplicación de Punto de Venta (PDV), una clase necesita conocer el gran total de la venta. Surge la pregunta: ¿Quién es el responsable de conocer este gran total? Para calcularlo, es necesario conocer todas las instancias de LineaDeVenta asociadas a una Venta y la suma de sus subtotales. Solo la instancia Venta posee esta información. Desde la perspectiva del patrón Experto en Información, Venta es la clase adecuada para asumir esta responsabilidad, ya que es la experta en la información relevante.

Contraindicaciones

En algunas ocasiones, este patrón no es deseable, especialmente debido a posibles problemas de cohesión, acoplamiento y duplicación de código. Por ejemplo, si una Venta debe ser almacenada en una base de datos, el patrón Experto en Información sugeriría a Venta como responsable, ya que contiene gran parte de la información a almacenar.

Patrones Relacionados

Bajo Acoplamiento, Alta Cohesión.

2. Creador

Pregunta

¿Quién debe ser responsable de la creación de una nueva instancia de una clase?

Respuesta

Una clase B tiene la responsabilidad de crear una instancia de la clase A si cumple alguna de las siguientes condiciones:

  • B agrega objetos de A.
  • B contiene objetos de A.
  • B almacena objetos de A.
  • B usa objetos de A.
  • B tiene los datos necesarios para inicializar A cuando este es creado.

Consideraciones

Soporta el bajo acoplamiento, es decir, no lo incrementa.

Ejemplo

En la aplicación PDV, ¿quién debería crear una LineaDeVenta? La clase Venta de hecho contiene (agrega) muchos objetos de LineaDeVenta. Por lo tanto, Venta debería tener la responsabilidad de crear instancias de esta clase y, consecuentemente, contener un método como crearLineaDeVenta.

Contraindicaciones

A menudo, la creación de instancias es una tarea compleja. En estos casos, es aconsejable delegar la creación a una clase auxiliar denominada Factoría.

Patrones Relacionados

Bajo Acoplamiento, Factoría.

3. Bajo Acoplamiento

Pregunta

¿Cómo fomentar una dependencia escasa y aumentar la reutilización?

Respuesta

Asignar responsabilidades de tal manera que el acoplamiento sea el menor posible.

Consideraciones

  • Mejor comprensión de las clases aisladas.
  • Facilitan la reutilización de código.
  • Los cambios en un componente no afectan a otros.

Ejemplo

Suponiendo que se necesita crear una instancia de Pago y asociarla a una Venta. ¿Qué clase se encargará de hacer esto? Puesto que una instancia de TPDV (Terminal Punto de Venta) registra un Pago, el patrón Creador indica que TPDV es un buen candidato para producir el Pago. La instancia TPDV podría entonces enviarle a Venta el mensaje agregarPago, transmitiendo al mismo tiempo el nuevo Pago como parámetro.

Patrones Relacionados

Variaciones Protegidas.

4. Alta Cohesión

Pregunta

¿Cómo mantener la complejidad dentro de límites manejables?

Respuesta

Asignar una responsabilidad de modo que la cohesión siga siendo alta.

Consideraciones

  • Incrementa la claridad y facilita la comprensión, simplificando el mantenimiento.
  • Implica casi siempre bajo acoplamiento, lo que aumenta la reutilización.

Ejemplo

Este ejemplo es una contradicción, es decir, va en contra del patrón de Alta Cohesión.

Patrones Relacionados

Bajo Acoplamiento.

5. Controlador

Pregunta

¿Quién debería encargarse de atender un evento del sistema?

Respuesta

Asignar la responsabilidad del manejo de un mensaje de los eventos de un sistema a una clase que represente una de las siguientes opciones:

  • El sistema global (controlador de fachada).
  • La empresa u organización global (controlador de fachada).
  • Algo en el mundo real que es activo y que pueda participar en la tarea (controlador de tareas).
  • Un manejador artificial de todos los eventos del sistema de un caso de uso (controlador de casos de uso).

Consideraciones

  • Potencial para reutilizar interfaces conectables.
  • Facilita el razonamiento sobre el funcionamiento de los casos de uso, validando la secuencia de operaciones y capturando información de estado.

Ejemplo

¿Quién debería ser el controlador de eventos sistémicos como introducirProducto y terminarVenta? De acuerdo con el patrón Controlador, disponemos de las siguientes opciones:

  • TPDV: representa el «sistema» global.
  • Tienda: representa la empresa u organización global.
  • Cajero: representa algo en el mundo real que está activo (por ejemplo, el papel de una persona) y que puede intervenir en la tarea.
  • ManejadorDeComprarProductos: representa un manejador artificial de todas las operaciones del sistema de un caso de uso.

Patrones Relacionados

Command, Fachada, Capas, Fabricación Pura.

6. Polimorfismo

Pregunta

¿Cómo manejar las alternativas basadas en el tipo? ¿De qué manera crear componentes de software conectables?

Respuesta

Cuando el comportamiento relacionado varía según el tipo (clase), asigne la responsabilidad para dicho comportamiento (utilizando operaciones polimórficas) a los tipos para los que varía el comportamiento. Evite las comprobaciones explícitas sobre el tipo de un objeto y procure no utilizar lógica condicional basada en tipos.

Consideraciones

  • Se añaden fácilmente extensiones necesarias para nuevas variaciones.
  • Las nuevas implementaciones se introducen sin afectar a los clientes.

Ejemplo

Patrones Relacionados

Variaciones Protegidas, y varios de los patrones de diseño GoF como Adaptador, Command, Composite, Proxy, Estado y Estrategia.

7. Fabricación Pura

Pregunta

¿Qué objetos deberían tener la responsabilidad cuando no se quiere violar los objetivos de alta cohesión y bajo acoplamiento?

Respuesta

Asigne un conjunto de responsabilidades altamente cohesivas a una clase artificial que no representa un concepto del dominio.

Consideraciones

  • Soporta alta cohesión puesto que las responsabilidades se factorizan en una clase de grano fino.
  • El potencial para reutilizar aumenta.

Ejemplo

Se necesita soporte para almacenar la Venta en una base de datos. Según el patrón Experto en Información, se podría suponer que la propia clase Venta debería tener esta responsabilidad. Sin embargo, esta tarea implica un amplio número de operaciones de base de datos no relacionadas directamente con las ventas, lo que resultaría en baja cohesión para la clase Venta. Además, Venta tendría que acoplarse con la interfaz de la base de datos. Finalmente, la tarea de almacenar objetos puede ser necesaria para más objetos que solo Venta. En su lugar, podríamos crear una clase AlmacenamientoPersistente.

Contraindicaciones

Llevar el principio al extremo.

Patrones Relacionados

Bajo Acoplamiento, Alta Cohesión, y varios patrones GoF (como Adaptador, Command, Estrategia, entre otros), así como prácticamente el resto de patrones de diseño.

8. Indirección

Pregunta

¿Dónde asignar una responsabilidad para evitar el acoplamiento directo entre dos objetos?

Respuesta

Asigne la responsabilidad a un objeto intermedio que medie entre otros componentes para que no estén acoplados directamente. Este intermediario crea una indirección.

Consideraciones

Disminuye el acoplamiento entre componentes.

Ejemplo

Muchos de los patrones existentes son especializaciones del patrón Indirección.

Patrones Relacionados

Está relacionado con Variaciones Protegidas, Bajo Acoplamiento, y muchos patrones GoF como Adaptador, Puente, Fachada, Observador y Mediador.

9. Variaciones Protegidas

Pregunta

¿Cómo diseñar componentes de manera que las variaciones en ellos no tengan repercusión en otros elementos?

Respuesta

Asigne responsabilidades para crear una interfaz estable alrededor de los puntos de variaciones previstas o inestabilidad.

Consideraciones

  • Se añaden fácilmente las extensiones para nuevas variaciones.
  • Se pueden introducir nuevas implementaciones sin afectar a los clientes.
  • Se reduce el acoplamiento.

Ejemplo

Mediante la Indirección de la interfaz y el Polimorfismo, se consigue una protección frente a las variaciones en las interfaces externas, integrando el sistema con otros sistemas externos.

Patrones Relacionados

La mayoría de los patrones son mecanismos de Variaciones Protegidas. Se relaciona con Polimorfismo, Indirección y la mayor parte de los patrones GoF.

Patrones de Diseño GoF: Creacionales y Estructurales

1. Singleton (Creación)

Pregunta

¿Cómo podemos asegurar que una clase tiene exactamente una única instancia y que esta sea fácilmente accesible?

Respuesta

Defina un método estático de la clase que devuelva la única instancia del Singleton.

Consideraciones

  • Permite el manejo de objetos únicos y que sean accesibles a otros objetos.
  • Proporciona acceso controlado a la única instancia.

Ejemplo

Patrones Relacionados

Se utiliza a menudo junto con Factoría y Fachada.

2. Factoría (Creación)

Pregunta

¿Quién es el responsable de la creación de objetos cuando existen consideraciones especiales, como una lógica de creación compleja o el deseo de separar las responsabilidades de la creación para mejorar la cohesión?

Respuesta

Cree un objeto de Fabricación Pura denominado Factoría (o Factoría Concreta) que resuelva la creación cuando existan consideraciones especiales.

Consideraciones

  • Separa la responsabilidad de la creación compleja en objetos de apoyo.
  • Oculta la lógica de creación potencialmente compleja.
  • Permite introducir estrategias para mejorar el rendimiento de la gestión de memoria.

Ejemplo

¿Quién crea un Adaptador y cuál debería crearse? Si esta responsabilidad se encarga a un objeto de dominio, excedería la lógica de la aplicación o disminuiría su cohesión. Resulta mejor asignar a una FactoriaDeServicios dicha responsabilidad, permitiendo que el Adaptador se cree dinámicamente. De esta manera, se podría cambiar de Adaptador sin modificar el código existente.

Patrones Relacionados

A menudo, se accede a las factorías mediante el patrón Singleton.

4. Adaptador (Wrapper – Estructural)

Pregunta

¿Cómo resolver interfaces incompatibles, o proporcionar una interfaz estable para componentes similares con diferentes interfaces?

Respuesta

Convierta 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.

Consideraciones

  • Posibilidad de reutilizar clases diseñadas para un objetivo específico pero con interfaces incompatibles con el sistema en desarrollo.
  • Un adaptador de objetos permite adaptar una clase y sus subclases, pudiendo funcionar para varios objetos adaptables.

Ejemplo

El Adaptador ofrece Variaciones Protegidas en paquetes de terceros mediante un objeto de Indirección que aplica Interfaces y Polimorfismo.

Participantes

  • Objetivo: Define la interfaz que utiliza el Cliente.
  • Cliente: Colabora con objetos que se ajustan a la interfaz Objetivo.
  • Adaptable: Define una interfaz existente que necesita ser adaptada.
  • Adaptador: Adapta la interfaz de Adaptable a la interfaz Objetivo.

Patrones Relacionados

Un adaptador de recursos oculta que un sistema externo podría también ser considerado un objeto Fachada, puesto que envuelve el acceso al subsistema o sistema con un único objeto (que es la esencia de la Fachada). Sin embargo, el nombre de Adaptador está motivado especialmente cuando el objeto que envuelve facilita la adaptación a diversas interfaces externas.

5. Composite (Estructural)

Pregunta

¿Cómo tratar un grupo o una estructura compuesta del mismo modo que un objeto atómico?

Respuesta

Defina las clases para los objetos compuestos y atómicos de manera que implementen la misma interfaz.

Consideraciones

  • Define jerarquías parte/todo.
  • Los clientes ignoran la diferencia entre objetos compuestos y objetos individuales que los forman, pudiendo tratar objetos primitivos y compuestos de modo uniforme.
  • Permite una jerarquía con clases que modelan objetos primitivos y objetos compuestos, facilitando la composición recursiva.
  • Es fácil añadir nuevos tipos de componentes.

Ejemplo

Se puede crear una estrategia que implemente la misma interfaz y que al mismo tiempo englobe varias estrategias, las cuales se van añadiendo en distintos puntos.

Participantes

  • Componente: Declara la interfaz para los objetos de la composición e implementa los métodos que determinan el comportamiento de las diferentes clases (tanto compuestas como hojas), permitiendo el acceso a componentes en la estructura recursiva.
  • Hoja: Define el comportamiento de los objetos que no tienen hijos en la composición.
  • Compuesto: Maneja los hijos de los diferentes componentes compuestos y determina el comportamiento de aquellos componentes capaces de tener hijos.
  • Cliente: Manipula los objetos de la composición a través del componente.

Patrones Relacionados

El patrón Composite se utiliza normalmente junto con los patrones Estrategia y Command. El Composite se basa en Polimorfismo y proporciona Variaciones Protegidas a los clientes, de manera que no les afecta si el objeto con el que se relacionan es atómico o compuesto.

8. Estrategia (Comportamiento)

Pregunta

¿Cómo diseñar diversos algoritmos o políticas que estén relacionadas? ¿Cómo diseñar para que estos puedan cambiar?

Respuesta

Defina cada algoritmo, política o estrategia en una clase independiente con una interfaz común.

Consideraciones

  • Permite configurar una clase con uno de varios comportamientos posibles.
  • Útil cuando se necesitan diferentes variantes de un algoritmo para un mismo comportamiento.
  • Elimina sentencias CASE cuando una clase define muchos comportamientos.
  • El cliente puede elegir entre diferentes estrategias o implementaciones, pero debe conocer los detalles que las diferencian.

Participantes

  • Estrategia: Interfaz común a todos los algoritmos permitidos.
  • Estrategia Concreta: Implementa los algoritmos correspondientes, usando la interfaz Estrategia.
  • Contexto: Mantiene una referencia al objeto Estrategia y se configura con un objeto Estrategia Concreta.

Ejemplo

Patrones Relacionados

Se crea mediante una Factoría a la que se accede como Singleton.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.