El patrón de diseño State
Descripción
El patrón de diseño State permite a un objeto adaptar su comportamiento en función de su estado interno.
Ejemplo
Nos interesamos a continuación por los pedidos de productos en nuestro sitio de venta online. Están descritos mediante la clase Pedido. Las instancias de esta clase poseen un ciclo de vida que se ilustra en el diagrama de estados y transiciones de la figura 25.1. El estado EnCurso se corresponde con el estado en el que el pedido está en curso de creación: el cliente agrega los productos. El estado Validado es el estado en el que el pedido ha sido validado y aprobado por el cliente. Por último, el estado Entregado es el estado en el que los productos han sido entregados.
Figura 25.1 - Diagrama de estados y transiciones de un pedido
La clase Pedido posee métodos cuyo comportamiento varía en función de este estado. Por ejemplo, el método agregaProducto solo permite agregar productos si el pedido se encuentra en el estado EnCurso. Así mismo, el método borra no tiene ninguna acción en los estados Entregado y Validado.
El enfoque tradicional para resolver estas diferencias en el comportamiento consiste en utilizar conexiones condicionales en el cuerpo de los métodos. Este enfoque conduce a menudo a métodos complejos de escribir y de mantener, ahogados por lo que yo llamo "bosques de ifs" (Taxus baccata para los iniciados).
El patrón de diseño State proporciona una solución más robusta que consiste en transformar cada estado en una clase. Esta...
Estructura
1. Diagrama de clases
La figura 25.3 ilustra la estructura genérica del patrón de diseño State.
![images/figure25-3.png images/figure25-3.png](https://www.eni-training.com/download/a7e0f116-7bba-4d53-b8e1-b6e04b2bdab7/images/figure25-3.png?id=AAEAAAD%2f%2f%2f%2f%2fAQAAAAAAAAAMAgAAAE1FbmkuRWRpdGlvbnMuTWVkaWFwbHVzLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAJ0VuaS5FZGl0aW9ucy5NZWRpYXBsdXMuQ29tbW9uLldhdGVybWFyawIAAAAHcGlzVGV4dAlwaWR0ZURhdGUBAA0CAAAABgMAAAAwdGVzdCB2Ml84IC0gZmZhNjQzOWQtZDZjYi00NzYxLWJhYTMtZWZkZDNiZDcwMWM25rSq0F7p2IgL)
Figura 25.3 - Estructura del patrón de diseño State
2. Participantes
Los participantes del patrón de diseño State son los siguientes:
-
MaquinaEstados (Pedido) es una clase concreta que describe los objetos que son máquinas de estados, es decir que poseen un conjunto de estados que pueden ser descritos mediante un diagrama de estados y transiciones. Esta clase contiene una referencia hacia una instancia de una subclase de Estado que define el estado en curso.
-
Estado (AbstractEstadoPedido) es una clase abstracta que incluye los métodos ligados al estado y que gestiona la asociación con la máquina de estados.
-
EstadoConcretoA y EstadoConcretoB (PedidoEnCurso, PedidoValidado y PedidoEntregado) son subclases concretas que implementan el comportamiento de los métodos relativos a cada estado.
3. Colaboraciones
La máquina de estados delega las llamadas a los métodos dependiendo del estado en curso hacia un objeto de estado.
La máquina de estados puede transmitir al objeto de estado una referencia hacia sí misma si es necesario. Esta referencia puede pasarse durante la delegación o en la propia inicialización del objeto de estado.
Dominios de aplicación
El patrón de diseño State se utiliza en los siguientes casos:
-
El comportamiento de un objeto depende de su estado.
-
La implementación de esta dependencia en el estado mediante conexiones condicionales resulta muy compleja.
Ejemplo en PHP
A continuación presentamos el ejemplo de la figura 25.2 escrito en PHP. La clase Pedido se describe a continuación. Los métodos agregaProducto, suprimeProducto y borra dependen del estado. Por consiguiente, su implementación consiste en llamar al método correspondiente de la instancia referenciada mediante estadoPedido.
El constructor de la clase inicializa el atributo estadoPedido con una instancia de la clase PedidoEnCurso. El método estadoSiguiente pasa al estado siguiente asociando una nueva instancia al atributo estadoPedido.
<?php
declare(strict_types=1);
namespace ENI\DesignPatterns\State;
class Pedido
{
protected array $productos = [];
protected AbstractEstadoPedido $estadoPedido;
public function __construct()
{
$this->estadoPedido = new PedidoEnCurso($this);
}
public function getProductos(): array
{
return $this->productos;
}
public function borraProductos(): void
{
$this->productos = [];
}
public function agrega(Producto $producto): void
{
$this->productos[] = $producto;
}
public function agregaProducto(Producto $producto): void
{
$this->estadoPedido->agregaProducto($producto);
}
public function retiraProducto(Producto $producto): void
{
$this->estadoPedido->retiraProducto($producto);
}
public function borra(): void
{
$this->estadoPedido->borra(); ...