El patrón de diseño Chain of Responsibility
Descripción
El patrón de diseño Chain of Responsibility construye una cadena de objetos de forma que si un objeto de la cadena no puede responder favorablemente a la solicitud, la transmite a su sucesor y así sucesivamente hasta que uno de los objetos de la cadena responde.
Ejemplo
Nos situamos en el marco de la venta de vehículos de ocasión. Cuando se muestra el catálogo de vehículos, el usuario puede solicitar una descripción de uno de los vehículos a la venta. Si no se encuentra esta descripción, el sistema debe devolver la descripción asociada al modelo de este vehículo. Si de nuevo esta descripción del modelo no se encuentra, se debe devolver la descripción asociada a la marca del vehículo. Si tampoco existe una descripción asociada a la marca entonces se envía una descripción por defecto.
De este modo, el usuario recibe en primer lugar la descripción más precisa disponible en el sistema.
El patrón de diseño Chain of Responsibility proporciona una solución para llevar a cabo este mecanismo. Consiste en enlazar los objetos entre ellos desde el más específico (el vehículo) al más general (la marca) para formar la cadena de responsabilidad que le da el nombre al patrón de diseño. La solicitud de la descripción se transmite a lo largo de la cadena hasta que un objeto pueda procesarla y enviar la descripción.
El diagrama de objetos UML de la figura 18.1 ilustra esta situación y muestra las distintas cadenas de responsabilidad (de izquierda a derecha).
Figura 18.1 - Diagrama de objetos de vehículos, modelos y marcas con los enlaces...
Estructura
1. Diagrama de clases
La figura 18.4 describe la estructura genérica del patrón de diseño Chain of Responsibility.
Figura 18.4 - Estructura del patrón de diseño Chain of Responsibility
2. Participantes
Los participantes del patrón de diseño Chain of Responsability son los siguientes:
-
Gestor (AbstractObjetoBasico) es una clase abstracta que implementa bajo la forma de una asociación la cadena de responsabilidad así como la interfaz de las solicitudes.
-
GestorConcreto1 y GestorConcreto2 (Vehiculo, Modelo y Marca) son las clases concretas que implementan el procesamiento de las solicitudes utilizando la cadena de responsabilidad si no pueden procesarlas.
-
Cliente (Usuario) crea la solicitud inicial en un objeto de una de las clases GestorConcreto1 o GestorConcreto2.
3. Colaboraciones
El cliente realiza la solicitud inicial a un gestor. La solicitud se propaga a lo largo de la cadena de responsabilidad hasta el momento en el que alguno de los gestores puede procesarla.
Dominios de aplicación
El patrón de diseño Chain of Responsibility se utiliza en los casos siguientes:
-
Una cadena de objetos gestiona una solicitud según un orden que se define dinámicamente.
-
La forma en que una cadena de objetos gestiona una solicitud no tiene por qué conocerse en sus clientes.
Ejemplo en PHP
Veamos ahora un ejemplo escrito en lenguaje PHP. La clase abstracta AbstractoObjetoBasico se describe a continuación. Implementa la cadena de responsabilidad mediante el atributo siguiente cuyo valor podemos fijar mediante el método setSiguiente. Los demás métodos se corresponden con las especificaciones presentadas en la figura 18.2.
<?php
declare(strict_types=1);
namespace ENI\DesignPatterns\ChainOfResponsibility;
abstract class AbstractObjetoBasico
{
protected self $siguiente;
public function devuelveDescripcion(): string
{
$resultado = $this->getDescripcion();
if (!empty($resultado)) {
return $resultado . PHP_EOL;
}
if (isset($this->siguiente)) {
return $this->siguiente->devuelveDescripcion();
} else {
return $this->descripcionPorDefecto() . PHP_EOL;
}
}
public function setSiguiente(self $siguiente): void
{
$this->siguiente = $siguiente;
}
private function descripcionPorDefecto(): string ...