Está en la página 1de 22

Decorator (patrón de diseño)

De Wikipedia, la enciclopedia libre


Saltar a navegación, búsqueda

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.

Contenido
[ocultar]

• 1 Problema que soluciona


• 2 Aplicabilidad
• 3 Implementación
o 3.1 Ejemplo C++
o 3.2 Ejemplo C#
o 3.3 Ejemplo Java
o 3.4 Ejemplo Python
o 3.5 Ejemplo de PHP

• 4 Enlaces externos

[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.
• Hay 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()");
}
}
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();
Patron Decorator

Introduccion:

Añade dinamicamente nuevas responsabilidades a un objeto, proporcionando


una alternativa flexible a la herencia para extender funcionalidad.

Nombre del patron:


Decorator (wrapper)

Clasificacion del patron:


Estructural

Intencion:

Proporcionar una forma flexible de introducir o eliminar funcionalidad de un


componente sin modificar su apariencia externa o su funcion.

Motivacion:

A veces se desea adicionar responsabilidades a un objeto pero no a toda la


clase. Las responsabilidades se pueden adicionar por medio de los mecanismos
de Herencia, pero este mecanismo no es flexible porque la responsabilidad es
adicionada estáticamente. La solución flexible es la de rodear el objeto con
otro objeto que es el que adiciona la nueva responsabilidad. Este nuevo objeto
es el Decorator.

Estructura:
Participantes:

Component: Define la interface de los objetos a los que se le puede adicionar


responsabilidades dinamicamente.

ConcreteComponent: Define el objeto al que se le puede adicionar una


responsabilidad.

Decorator: Mantiene una refeencia al objeto Component y define una


interface de acuerdo con la interface de Component.

ConcreteDecorator: Adiciona la responsabilidad al componente.


El patrón Decorator y una aplicación real

Este post está directamente relacionado con el anterior. Yo sabía que no


todos lo iban a entender o que les iba a gustar. Fue así como el único comentario
prácticamente me quería boxear (pero con onda). Por cierto, recomiendo su blog. Lo
bueno de todo esto es que todos los días se aprende algo nuevo. Del comentario,
surgieron otros frameworks, de los cuales me gustó la propuesta de la empresa Open
Symphony, SiteMesh.

Este framework implementa el patrón Decorator de la GangOfFour. Este patrón dice


(básicamente) que podríamos a partir de una salida estándar a un usuario, modificarla
para adaptar esta salida a otros tipos de usuarios. Por ejemplo, en el caso de SiteMesh,
teniendo como punto de partida una página HTML, podemos "transformarla" en otro tipo
de salida (para imprimir, PDF, WML, etc.).

Para implementarlo utiliza una funcionalidad de los Servlets que siempre me ha gustado,
pero que no he tenido la oportunidad de usar en algo serio: Los Filtros.
Los filtros son una funcionalidad en J2EE que nos permite implementar otro patrón
(intercepting filter). Consiste en la posibilidad de realizar procesamiento de una o más
peticiones antes y después de ejecutar un servlet determinado.
De esta manera, cuando un servlet/jsp genera una salida HTML, teniendo configurado
SiteMesh (no más de 5 minutos), más uno o más archivos decoradores, es posible que la
salida generada se adapte al usuario.

Lo mejor de SiteMesh es que no lleva más de 1 hora / hora y media de lectura para
aprenderlo. Muy positivo.
Patrón de diseño Decorator en PHP5

Visitante escribió "Desde DesarrolladorSenior, Curso de Patrones de Diseño en


PHP5 donde vemos el uso del Patrón Decorator.

Un Decorator responde a la necesidad de agregar funcionalidad a un objeto por


medio de la asociación de clases. Es un objeto con el que se puede ejecutar
funcionalidades de varias clases a la vez. 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.

El uso de este patrón es equivalente a utilizar la herencia múltiple pero sin los
problemas que esta conlleva como son: las colisiones de nombres y la herencia
repetida.
PATRÓN DE DISEÑO DECORATOR EN PHP5
sábado, septiembre 12, 2009 Arley Triana Morín

Patrón Decorator responde a la necesidad de agregar funcionalidad a un objeto por medio de


la asociación de clases. Es un objeto con el que se puede ejecutar funcionalidades de varias
clases a la vez.

"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."
[1]

Clasificación del patrón

Estructural.

Intención o 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." [1]

Entonces tenemos las siguientes opciones:

1. 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.

2. Crear una subclase de VentanaConBorde: VentanaConBordeYBotonDeAyuda.


Problema: No tenemos una ventana con botón de ayuda y sin borde.
3. 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!.

Aplicabilidad

1. Añadir objetos individuales de forma dinámica y transparente.


2. Responsabilidades de un objeto pueden ser retiradas.
3. Cuando la extensión mediante la herencia no es viable.
4. Hay una necesidad de extender la funcionalidad de una clase, pero no hay razones
para extenderlo a través de la herencia.
5. Hay la necesidad de extender dinámicamente la funcionalidad de un objeto y quizás
quitar la funcionalidad extendida. [1]

Código de Ejemplo en PHP5


view plaincopy to clipboardprint?
1. <?php
2. interface iCoffee {
3. public function getBaseCost();
4. }
5.
6. class Coffee implements iCoffee {
7. protected $_baseCost = 0;
8. public function getBaseCost()
9. {
10. return $this->_baseCost;
11. }
12. }
13.
14. class BlackCoffee extends Coffee {
15. public function __construct()
16. {
17. $this->_baseCost = 5;
18. }
19. }
20.
21. abstract class CoffeeDecorator implements iCoffee {
22. protected $_coffee;
23. public function __construct(iCoffee $Coffee)
24. {
25. $this->_coffee = $Coffee;
26. }
27. }
28.
29. class WithCream extends CoffeeDecorator {
30. public function getBaseCost()
31. {
32. return $this->_coffee->getBaseCost() + 1.5;
33. }
34. }
35.
36. class WithMilk extends CoffeeDecorator {
37. public function getBaseCost()
38. {
39. return $this->_coffee->getBaseCost() + 4;
40. }
41. }
42.
43. class WithChocolate extends CoffeeDecorator {
44. public function getBaseCost()
45. {
46. return $this->_coffee->getBaseCost() + 5;
47. }
48. }
49.
50. $coffee = new WithChocolate(new WithMilk(new WithCream(new BlackCoffee())
));
51. echo 'El precio del cafe es: $' . $coffee->getBaseCost();
<?php<br /> interface iCoffee {<
Decorator Pattern (Patrón Decorador)
7 agosto, 2009 in Patterns and Practices | Tags: Decorator Pattern, Ingeniería del Software,
Patterns & Practices

Continuando con la temática de patrones de diseño software, hoy me propongo explicar


el patrón Decorador.
En ocasiones, durante la etapa de creación del diseño lógico de nuestra aplicación, podemos
encontrarnos que necesitamos poder extender la funcionalidad de una clase en tiempo de
ejecución.
Tambien podemos encontrarnos en la tesitura de necesitar que cierta funcionalidad pueda
ser “asociada” a diferentes componentes. Es en estos contextos donde el patrón Decorador
puede salvarnos!

Un ejemplo muy utilizado para explicar éste patrón es el ejemplo de una Ventana del
sistema. Podemos tener ventanas sin borde, con borde, ventanas que proporcionen un icono
con ayuda, ventanas que proporcionen un botón para minimizar a la barra de notificaciones,
etc..
Bien, uno podría pensar en crear una clase base llamada Ventana con las funcionalidades
básicas de este objeto. Al necesitar una ventana con bordes, podríamos pensar en crear una
clase llamada VentanaConBorde que herede de la clase Ventana. Hasta aquí perfecto.
Ahora necesitamos tambien una ventana que contenga un icono de ayuda. Volveríamos a
heredar de Ventana para crear la clase VentanaConIcono. Pero claro, yo quiero tener una
ventan con borde e icono. Asi pues tambien debo crear otra clase que herede de
VentanaConBorde y que tenga el icono. Esta clase se podría llamar
VentanaConBordeEIcono. Una vez hecho esto, nos damos cuenta de que tambien podemos
crear ventanas con un botón que minimice a la barra de notificaciones, pero claro pueden
ser ventanas con o sin borde y con o sin icono de ayuda (con todas sus combinaciones).

Nos podemos dar cuenta de que con la herencia de objetos no podremos solucionar (de una
forma simple y mantenible) este problema. Aquí es donde el patrón Decorador nos
ayudará.

Para empezar, éste es el esquema de la solución que propone este patrón:


Muestra las clases que aplican al patrón Decorador y las relaciones existentes entre ellas

Descripción de los elementos del diagrama:

Component: es una clase abstracta que englobará cualquier elemento que puede ser
decorado (en el ejemplo anterior, sería una clase AbstractVentana que incluiría los métodos
que definien la interfaz para dibujar los elementos).

ConcreteComponent: clase que hereda de Component y que define un elemento que puede
ser decorado (la clase Component es abstracta, por lo que no puede instanciarse). Es clase
implementará las operaciones de la clase Component dando una primera funcionalida base
(en el ejemplo anterior, correspondería con la clase Ventana, y el método dibujaría una
ventana básica, sin bordes ni nada parecido).

Decorator: es la clase base para cualquier decorador que vayamos a utilizar. Se trata de una
clase abstracta que será agregada (las clases que la hereden) a cada componente (en el
ejemplo, sería una clase AbstractDecorator que no implementaría los métodos de la base,
pero que tendría como atributo el componente al que se está aplicando (a nivel de
instancia)).

ConcreteDecorator: se trata de una clase que hereda de Decorator y que define una
funcionalidad concreta a añadir al componente (en el ejemplo podría ser añadir un borde,
un icono o un botón. Cada una de ellas sería una clase que deriva de Decorator y que puede
asociarse a cualquier Ventana).

Un ejemplo práctico

Con tal de que el patrón quede claro, voy a mostrar un pequeño ejemplo de código en el
que utilizo este patrón.

El ejemplo que voy a desarrollar se trata de elaborar iconos a los que se les pueda añadir
otros iconos informando de detalles concretos (por ejemplo, en el sistema operativo
Windows Vista, cuando una aplicación requiere de aprobación del Administrador, su icono
está “decorado” con otro pequeño icono en forma de escudo en los 4 colores del icono de
windows). Viene a ser el mismo ejemplo. Queremos poder decorar ciertos iconos en
función de las características que se apliquen a ese icono.

Esta clase se corresponde con la clase Component del diagrama de clases UML para el
patrón Decorator.

En segundo lugar, pasamos a crear la clase AbstractDecorator que será la clase base para
todos aquellos decoradores que puedan aplicarse a un icono (en realidad no es más que otro
icono que dibujaremos encima del original, en una posición concreta).
Esta clase se corresponde con la clase Decorator del diagrama de clases UML del patrón
Decorator.

Ahora que ya hemos definido las clases base, podemos crear las clases que heredarán de
estas y que serán las que nos proporcionarán toda la funcionalidad. Emepezaremos
definiendo un decorador concreto:
Esta clase se corresponde con la clase ConcreteDecorator del diagrama de clases UML del
patrón Decorator.

Y ahora una clase que herede de la clase AbstractIcon y que admita decoradores:
Esta clase se corresponde con la clase ConcreteComponent del diagrama de clases UML
para el patrón Decorator.

Por último os dejo el código de un formulario Winform en el que se hace uso de todas estas
clases para dibujar un icono al que, al pulsar un botón del formulario, se le añade un
decorador.
Simplemente
remarcar que, por el diseño que hemos realizado, un decorador puede contener otros
decoradores, lo que nos proporciona un enorme abanico de posibilidades y combinaciones.

Y eso es todo sobre este patrón. Como veis, en determinadas situaciones puede
resultarnosde mucha utilidad, como todos los patrones de diseño.
Como siempre, espero que este artículo os resulte útil e interesante!

¡Hasta otra!

También podría gustarte