En la edición anterior hicimos foco en el patrón de comportamiento OBSERVER. En esta entrega vamos a hacer foco en el patrón estructural DECORATOR. Allá vamos!
Como comenté en el artículo introductorio a los patrones de diseño (acá el link), un patrón estructural es aquel que especifica la forma en que las clases se relacionan con otras.
También vimos que hay 4 elementos que definen un patrón. Así que vamos a utilizarlos para exponer el patrón DECORATOR.
Patrón DECORATOR
Nombre: DECORATOR
Problema: La funcionalidad de un objeto depende de muchas variables que se definen en tiempo de ejecución.
Solución: Agregar dichas funcionalidades extras en tiempo de ejecución creando envolturas que contengan al objeto.
Consecuencias:
- Ventajas: Al agregar en tiempo de ejecución y no en tiempo de compilación, evitamos crear muchas clases para todos los posibles casos o de agregar todo el comportamiento a una clase que muy probablemente no use. Es transparente para el usuario final ya que para él se está comunicando con el objeto inicial.
- Desventajas: Pueden complicar el proceso de instanciar un objeto ya que hay que tener en cuenta todas las envolturas que este va a necesitar.
Ejemplo
Un ejemplo que me parece super claro es con la carga de pizzas de una pizzería. Uno tiene diferentes tipos de pizzas (cada una con su valor). También tenemos diferentes tipos de extras (cada uno con su precio) y se les puede agregar todos los que se requieran a cada pizza. Aplicando este patrón, el diagrama de clases nos quedaría de la siguiente manera:
Al momento de cargar una pizza de muzzarella simplemente se crea un objeto de clase PizzaMuzzarella. Pero al momento de necesitar una pizza de muzzarella con dos extras de Muzzarella se tienen que crear tres objetos. Uno de clase PizzaMuzzarella y dos de clase ExtraMuzzarella. El proceso sería crear la pizza, luego crear un extra y al extra se le pasa la pizza. Luego se crea el otro extra y se le pasa el extra anterior que ya tiene la pizza.
A la hora de calcular el costo de la pizza con los extras se le pide al objeto con el que interactúa el usuario final que calcule su costo a través del método calcularCosto(). Como cada clase sabe su precio, sólo tiene que preguntarle al que contiene su precio para devolver el total.
Conclusión
Es un patrón muy útil a la hora de diseñar una posible solución a objetos que dependen de muchas variables en tiempo de ejecución. Nos salva de tener que crear una clase que tenga toda la funcionalidad "por las dudas" y terminaríamos usando el anti-patrón BLOB. También nos salva de tener que hacer una clase para cada posible caso en tiempo de ejecución, que como dijimos anteriormente son muchas las posibilidades.
Nótese que, además de usar el patrón DECORATOR, creamos clases para los diferentes tipos de pizzas y extras para que cada una sepa su precio (sin importar como lo consiga es su responsabilidad). Esto hace que a la hora de modificar un precio hay que modificar una sola clase.
A esto me refiero cuando digo que hay que entender los patrones para poder aplicarlos a nuestro problema particular.
Con eso cierro mi DECORATOR++ y los espero en el próximo artículo!