Decorator (patrón de diseño)
El patrón Decorator responde a la necesidad de añadir dinámicamente funcionalidad a un Objeto. Esto nos permite no tener que crear sucesivas clases que hereden de la primera incorporando la nueva funcionalidad, sino otras que la implementan y se asocian a la primera.
Índice |
[editar] Problema que soluciona
Supongamos que tenemos una clase existente Ventana y queremos añadirle funcionalidad para que muestre un borde alrededor. Podemos crear una subclase VentanaConBorde que herede de Ventana.
Hasta aquí todo bien, pero supongamos que surge la necesidad de crear una ventana que muestre un pequeño botón de ayuda con un signo de interrogación (?) en su parte superior. Entonces tenemos las siguientes opciones:
- Crear otra subclase de Ventana: VentanaConBotónDeAyuda.
Problema: No cubre la necesidad de tener ventanas con bordes y botón de ayuda a la vez.
- Crear una subclase de VentanaConBorde: VentanaConBordeYBotonDeAyuda.
Problema: No tenemos una ventana con botón de ayuda y sin borde.
- Crear clases para todas las combinaciones posibles de funcionalidades.
Problema: Con este ejemplo tendríamos cuatro clases: Ventana, VentanaConBorde, VentanaConBotonDeAyuda y VentanaConBordeYBotonDeAyuda; con tres funcionalidades tendríamos ocho clases y con cuatro, ¡dieciséis!
[editar] Aplicabilidad
- Añadir objetos individuales de forma dinámica y transparente
- Responsabilidades de un objeto pueden ser retiradas
- Cuando la extensión mediante la herencia no es viable
- Hay una necesidad de extender la funcionalidad de una clase, pero no hay razones para extenderlo a través de la herencia.
- Existe la necesidad de extender dinámicamente la funcionalidad de un objeto y quizás quitar la funcionalidad extendida.
[editar] Implementación
El patrón Decorator soluciona este problema de una manera mucho más sencilla y extensible.
Se crea a partir de Ventana la subclase abstracta VentanaDecorator y, heredando de ella, BordeDecorator y BotonDeAyudaDecorator. VentanaDecorator encapsula el comportamiento de Ventana y utiliza composición recursiva para que sea posible añadir tantas "capas" de Decorators como se desee. Podemos crear tantos Decorators como queramos heredando de VentanaDecorator.
[editar] Ejemplo C++
#include <iostream> // Portado del ejemplo de C# por Ricardo M. García //[Clase Component] ver diagrama de clases class IVentanaAbstracta { public: virtual void Dibujar() = 0; }; //[Clase ConcreteComponent] ver diagrama de clases, Clase que se desea decorar class Ventana : public IVentanaAbstracta { public: void Dibujar() { std::cout << " Ventana "; } }; //[Clase Decorator] ver diagrama de clases class VentanaDecorator : public IVentanaAbstracta { public: VentanaDecorator(IVentanaAbstracta* ventanaAbstracta) { _VentanaAbstracta = ventanaAbstracta; } virtual void Dibujar() = 0; protected: IVentanaAbstracta* _VentanaAbstracta; }; //[Clase ConcreteDecorator] ver diagrama de clases class BordeDecorator : public VentanaDecorator { public: BordeDecorator(IVentanaAbstracta* ventanaAbstracta) : VentanaDecorator(ventanaAbstracta) { } virtual void Dibujar() { std::cout << "|"; _VentanaAbstracta->Dibujar(); std::cout << "|"; } }; //[Clase ConcreteDecorator] ver diagrama de clases class BotonDeAyudaDecorator : public VentanaDecorator { public: BotonDeAyudaDecorator(IVentanaAbstracta* ventanaAbstracta) : VentanaDecorator(ventanaAbstracta) { } virtual void Dibujar() { _VentanaAbstracta->Dibujar(); std::cout << "[Boton de Ayuda]"; } }; int main() { BotonDeAyudaDecorator* ventanaConBotonDeAyuda = new BotonDeAyudaDecorator(new Ventana()); ventanaConBotonDeAyuda->Dibujar(); std::cout << std::endl; BordeDecorator* ventanaConBotonDeAyudaYBorde = new BordeDecorator(ventanaConBotonDeAyuda); ventanaConBotonDeAyudaYBorde->Dibujar(); std::cout << std::endl; BordeDecorator* ventanaConBorde = new BordeDecorator(new Ventana()); ventanaConBorde->Dibujar(); std::cout << std::endl; BordeDecorator* ventanaConDobleBorde = new BordeDecorator(ventanaConBorde); ventanaConDobleBorde->Dibujar(); std::cout << std::endl; delete ventanaConBotonDeAyuda; delete ventanaConBotonDeAyudaYBorde; delete ventanaConBorde; delete ventanaConDobleBorde; return 0; }
[editar] Ejemplo C#
using System; using System.Collections.Generic; using System.Text; namespace Decorator { class Program { static void Main(string[] args) { BotonDeAyudaDecorator ventanaConBotonDeAyuda = new BotonDeAyudaDecorator(new Ventana()); ventanaConBotonDeAyuda.Dibujar(); Console.WriteLine(); BordeDecorator ventanaConBotonDeAyudaYBorde = new BordeDecorator(ventanaConBotonDeAyuda); ventanaConBotonDeAyudaYBorde.Dibujar(); Console.WriteLine(); BordeDecorator ventanaConBorde = new BordeDecorator(new Ventana()); ventanaConBorde.Dibujar(); Console.WriteLine(); BordeDecorator ventanaConDobleBorde = new BordeDecorator(ventanaConBorde); ventanaConDobleBorde.Dibujar(); Console.WriteLine(); Console.Read(); } //[Clase Component] ver diagrama de clases public interface IVentanaAbstracta { void Dibujar(); } //[Clase ConcreteComponent] ver diagrama de clases, Clase que se desea decorar public class Ventana : IVentanaAbstracta { public void Dibujar() { Console.Write(" Ventana "); } } //[Clase Decorator] ver diagrama de clases public abstract class VentanaDecorator : IVentanaAbstracta { public VentanaDecorator(IVentanaAbstracta ventanaAbstracta) { _VentanaAbstracta = ventanaAbstracta; } protected IVentanaAbstracta _VentanaAbstracta; public abstract void Dibujar(); } //[Clase ConcreteDecorator] ver diagrama de clases public class BordeDecorator : VentanaDecorator { public BordeDecorator(IVentanaAbstracta ventanaAbstracta) : base(ventanaAbstracta) { } public override void Dibujar() { Console.Write("|"); _VentanaAbstracta.Dibujar(); Console.Write("|"); } } //[Clase ConcreteDecorator] ver diagrama de clases public class BotonDeAyudaDecorator : VentanaDecorator { public BotonDeAyudaDecorator(IVentanaAbstracta ventanaAbstracta) : base(ventanaAbstracta) { } public override void Dibujar() { _VentanaAbstracta.Dibujar(); Console.Write("[Boton de Ayuda]"); } } } }
[editar] Ejemplo Java
abstract class Componente{ abstract public void operacion(); } class ComponenteConcreto extends Componente{ public void operacion(){ System.out.println("ComponenteConcreto.operacion()"); } } abstract class Decorador extends Componente{ protected Componente componente; public void setComponente(Componente component){ this.componente = component; } public void operacion(){ if (componente != null) componente.operacion(); } } class DecoradorConcretoA extends Decorador{ private String estadoAgregado; public void operacion(){ super.operacion(); estadoAgregado = "Nuevo estado"; System.out.println("DecoradorConcretoA.operacion()"); System.out.println("Java es lo mejor"); } } class DecoradorConcretoB extends Decorador{ public void operacion(){ super.operacion(); agregarComportamiento(); System.out.println("DecoradorConcretoB.operacion()"); } void agregarComportamiento(){ System.out.println("comportamiento añadido B"); } } public class Cliente{ public static void main(String[] args){ ComponenteConcreto c = new ComponenteConcreto(); DecoradorConcretoA d1 = new DecoradorConcretoA(); DecoradorConcretoB d2 = new DecoradorConcretoB(); d1.setComponente(c); d2.setComponente(d1); d2.operacion(); } }
[editar] Ejemplo Python
def establecer_costo_decorator(funcion): def envoltorio1(instancia, costo): funcion(instancia, costo) return envoltorio1 def obtener_costo_decorator(costo_adicional): def envoltorio1(funcion): def envoltorio2(instancia): return funcion(instancia) + costo_adicional return envoltorio2 return envoltorio1 class Cafe(object): @establecer_costo_decorator def establecer_costo(self, costo): self.costo = costo @obtener_costo_decorator(0.5) @obtener_costo_decorator(0.7) @obtener_costo_decorator(0.2) def obtener_costo(self): return self.costo cafe = Cafe() cafe.establecer_costo(1.0) print cafe.obtener_costo() # 2.4
[editar] Ejemplo de PHP
<?php interface iCoffee { public function getBaseCost(); } class Coffee implements iCoffee { protected $_baseCost = 0; public function getBaseCost() { return $this->_baseCost; } } class BlackCoffee extends Coffee { public function __construct() { $this->_baseCost = 5; } } abstract class CoffeeDecorator implements iCoffee { protected $_coffee; public function __construct(iCoffee $Coffee) { $this->_coffee = $Coffee; } } class WithCream extends CoffeeDecorator { public function getBaseCost() { return $this->_coffee->getBaseCost() + 1.5; } } class WithMilk extends CoffeeDecorator { public function getBaseCost() { return $this->_coffee->getBaseCost() + 4; } } class WithChocolate extends CoffeeDecorator { public function getBaseCost() { return $this->_coffee->getBaseCost() + 5; } } $coffee = new WithChocolate(new WithMilk(new WithCream(new BlackCoffee()))); echo 'El precio del cafe es: $' . $coffee->getBaseCost();