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.
Un ejemplo para poder ver la aplicabilidad del patrón decorador podría ser el siguiente:
Define la interfaz para los objetos que pueden tener responsabilidades añadidas.
Define un objeto al cual se le pueden agregar responsabilidades adicionales.
Mantiene una referencia al componente asociado. Implementa la interfaz de la superclase Componente delegando en el componente asociado.
Añade responsabilidades al componente.
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.
//[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;
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Decorator
{
class Program
{
static void Main(string[] args)
{
BotonDeAyudaDecorator ventanaConBotonDeAyuda = new BotonDeAyudaDecorator(new Ventana());
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();
BordeDecorator ventanaConDobleBordeYBotonDeAyuda = new BordeDecorator(new BordeDecorator(ventanaConBotonDeAyuda));
ventanaConDobleBordeYBotonDeAyuda.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]"); }
}
}
}
public abstract class Componente{
abstract public void operacion();
}
public class ComponenteConcreto extends Componente{
public void operacion(){
System.out.println("ComponenteConcreto.operacion()");
}
}
public abstract class Decorador extends Componente{
private Componente componente;
public Decorador(Componente componente){
this.componente = componente;
}
public void operacion(){
componente.operacion();
}
}
public class DecoradorConcretoA extends Decorador{
private String propiedadAñadida;
public DecoradorConcretoA(Componente componente){
super(componente);
}
public void operacion(){
super.operacion();
this.propiedadAñadida = "Nueva propiedad";
System.out.println("DecoradorConcretoA.operacion()");
}
}
public class DecoradorConcretoB extends Decorador{
public DecoradorConcretoB(Componente componente){
super(componente);
}
public void operacion(){
super.operacion();
comportamientoAñadido();
System.out.println("DecoradorConcretoB.operacion()");
}
public void comportamientoAñadido(){
System.out.println("Comportamiento B añadido");
}
}
public class Cliente{
public static void main(String[] args){
ComponenteConcreto c = new ComponenteConcreto();
DecoradorConcretoA d1 = new DecoradorConcretoA(c);
DecoradorConcretoB d2 = new DecoradorConcretoB(d1);
d2.operacion();//output: "ComponenteConcreto.operacion()\n DecoradorConcretoA.operacion()\n Comportamiento B añadido\n DecoradorConcretoB.operacion()"
}
}
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
<?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();
Dividimos la implementación del decorador en un paquete llamado PaqueteDecorador.bpl (código a continuación)
unit PruebaDecorador;
interface
type
IBebida = interface
['{C58DD7FD-EA4A-4419-A9DF-CED5D260810A}']
function getDescripcion : String;
function precio : Real;
end;
TCafe = class(TInterfacedObject, IBebida)
private
FDescripcion : String;
public
constructor Create;
function getDescripcion : String;
function precio : Real;
end;
TLeche = class(TInterfacedObject, IBebida)
private
FDescripcion : String;
public
constructor Create;
function getDescripcion : String;
function precio : Real;
end;
TIngredientes = class(TInterfacedObject, IBebida)
function getDescripcion : String; virtual; abstract;
function precio : Real; virtual; abstract;
end;
TChocolate = class(TIngredientes)
private
FDescripcion : String;
FBebida : IBebida;
public
constructor Create(TObject : IBebida);
function getDescripcion : String; override;
function precio : Real; override;
end;
TCereales = class(TIngredientes)
private
FDescripcion : String;
FBebida : IBebida;
public
constructor Create(TObject : IBebida);
function getDescripcion : String; override;
function precio : Real; override;
end;
implementation
uses
Spring.Container,
Spring.Services
;
{ TBebida }
constructor TCafe.Create;
begin
FDescripcion := 'Cafe';
end;
function TCafe.getDescripcion: String;
begin
Result := FDescripcion;
end;
function TCafe.precio: Real;
begin
Result := 1.00;
end;
{ TChocolate }
constructor TChocolate.Create(TObject: IBebida);
begin
FDescripcion := 'Chocolate (ing)';
FBebida := TObject;
end;
function TChocolate.getDescripcion: String;
begin
Result := FDescripcion + ' ' + FBebida.getDescripcion;
end;
function TChocolate.precio: Real;
begin
Result := 0.5 + FBebida.precio;
end;
{ TCereales }
constructor TCereales.Create(TObject: IBebida);
begin
FDescripcion := 'Cereales (ing)';
FBebida := TObject;
end;
function TCereales.getDescripcion: String;
begin
Result := FDescripcion + ' ' + FBebida.getDescripcion;
end;
function TCereales.precio: Real;
begin
Result := 0.2 + FBebida.precio;
end;
{ TLeche }
constructor TLeche.Create;
begin
FDescripcion := 'Leche';
end;
function TLeche.getDescripcion: String;
begin
Result := FDescripcion;
end;
function TLeche.precio: Real;
begin
Result := 0.75;
end;
initialization
GlobalContainer.RegisterType<TCafe>.Implements<IBebida>('Cafe');
GlobalContainer.RegisterType<TLeche>.Implements<IBebida>('Leche');
GlobalContainer.RegisterType<TChocolate>.Implements<IBebida>('Chocolate');
GlobalContainer.RegisterType<TCereales>.Implements<IBebida>('Cereales');
GlobalContainer.Build;
end.
y el código desde el que se invoca el paquete
program ConsolaDecorador;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Spring.Services, // ServiceLocator
PruebaDecorador;
var
cafe, leche: IBebida;
begin
try
cafe := ServiceLocator.GetService<IBebida>('Cafe');
cafe := TChocolate.Create(cafe);
cafe := TCereales.Create(cafe);
writeln(cafe.precio);
writeln(cafe.getDescripcion);
leche := ServiceLocator.GetService<IBebida>('Leche');
leche := TChocolate.Create(leche);
leche := TChocolate.Create(leche);
writeln(leche.precio);
writeln(leche.getDescripcion);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.