Imagina que quieres «decorar» un objeto añadiendole multitud de objetos relacionados. Pongamos por ejemplo que queremos hacer una carta para un restaurante. Podríamos crear una clase llamada Comida con multitud de campos que señalasen la cantidad de cada ingrediente. Esta clase comida nos serviría para representar cualquier plato… a primera vista.
Podríamos sobrevivir con este esperpento de código… la primera semana. En cuanto queramos escalar la aplicación para añadir nuevos ingredientes, nos encontraremos entrando y saliendo del código fuente, por no hablar de los cambios en el precio. Por supuesto podríamos componer esta clase con infinidad de objetos, pero afortunadamente existe un patrón que no solo te permite componer una comida con los ingredientes justos y necesarios, sino que encima te permite crear cualquier tipo de plato en tiempo de ejecución.
Se acabó el crear una clase para cada objeto. Ahora podemos tener una clase que se componga de elementos a medida que los vayamos necesitando, algo similar al patrón Strategy pero acumulativo.
¿Hasta ahora se parece mucho a un patron Strategy no? Pero nos falta poder hacer el componente Comida de los ingredientes acumulativo y no es muy complejo, pero hay que poner atención.
- Pasaremos la referencia de un ingrediente (que contendrá la comida acumulada) al nuevo ingrediente para actualizar su atributo Comida. De esta forma, cada ingrediente que creemos guardará su valor.
- Extenderemos los ingredientes para que hereden de Comida. Si no lo hacemos, los objetos que contienen la comida y que estamos pasando a otros constructores no podrían ser casteados al tipo del atributo.
Puede resultar bastante lioso en un principio, sobre todo si es la primera vez que lo ves, así que recapitulemos:
- Tenemos una clase Comida que queremos decorar.
- Creamos los decoradores, que son los ingredientes.
- Los ingredientes se crean obteniendo la referencia de una comida y guardándola en un atributo.
- Para acumular los ingredientes.
- Hacemos que extiendan Comida.
- Pasamos el antiguo ingrediente en el constructor del nuevo y guardamos su referencia.
- Repetimos el paso anterior hasta que queramos.
En un código muy sencillo (quitando cualquier tipo de abstracción) sería:
Fijaros en que para calcular los valores teniendo en cuenta las comidas acumuladas, se usa el atributo que hemos añadido para recuperar el valor, provocando un «efecto cascada».
Os recomiendo copiar el código de arriba y debuguearlo para comprender el flujo del programa y del patrón.
¿Cómo podríamos mejorar este código?
Si quisieramos crear implementaciones concretas de comida (comidas ya predefinidas) podríamos abstraer la clase comida y utilizar los ingredientes sobre estas clases, como muestra el siguiente UML más reconocible del patrón.