Mi granito de java: Decorator

sábado, 4 de junio de 2011

Decorator

El patrón decorator permite añadir responsabilidades a objetos concretos de forma dinámica. Los decoradores ofrecen una alternativa más flexible que la herencia para extender las funcionalidades.
Es conocido como Wrapper (igual que el patrón Adapter).
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.

Este patrón se debe utilizar cuando:
  • Hay una necesidad de extender la funcionalidad de una clase, pero no hay razones para extenderlo a través de la herencia.
  • Se quiere agregar o quitar dinámicamente la funcionalidad de un objeto.

Dado que este patrón decora un objeto y le agrega funcionalidad, suele ser muy utilizado para adicionar opciones de "embellecimiento" en las interfaces al usuario. Este patrón debe ser utilizado cuando la herencia de clases no es viable o no es útil para agregar funcionalidad. Imaginemos que vamos a comprar una PC de escritorio. Una estándar tiene un precio determinado. Pero si le agregamos otros componentes, por ejemplo, un lector de CD, el precio varía. Si le agregamos un monitor LCD, seguramente también varía el precio. Y con cada componente adicional que le agreguemos al estándar, seguramente el precio cambiará. Este caso, es un caso típico para utilizar el Decorator.


Diagrama UML



Component: define la interface de los objetos a los que se les pueden adicionar responsabilidades dinámicamente.
ComponenteConcreteo: define el objeto al que se le puede adicionar una responsabilidad.
Decorator: mantiene una referencia al objeto Component y define una interface de acuerdo con la interface de Component.
DecoratorConcreto: adiciona la responsabilidad al Component.

Decorator propaga los mensajes a su objeto Component. Opcionalmente puede realizar operaciones antes y después de enviar el mensaje.


Ejemplo

Imaginemos que vendemos automóviles y el cliente puede opcionalmente adicionar ciertos componentes (aire acondicionado, mp3 player, etc). Por cada componente que se adiciona, el precio varía.




Bien, hasta aquí clases comunes de negoci: una interface que implementa la clase Auto y dos tipos de Auto (Ford y Fiat). Ahora veremos en que consiste el Decorator y los decoradores:





Veremos como funciona desde el punto de vista del cliente:





Consecuencias
  • Es más flexible que la herencia: utilizando diferentes combinaciones de unos pocos tipos distintos de objetos decorator, se puede crear muchas combinaciones distintas de comportamientos. Para crear esos diferentes tipos de comportamiento con la herencia se requiere que definas muchas clases distintas.
  • Evita que las clases altas de la jerarquía estén demasiado cargadas de funcionalidad.
  • Un componente y su decorador no son el mismo objeto.
  • Provoca la creación de muchos objetos pequeños encadenados, lo que puede llegar a complicar la depuración.
  • La flexibilidad de los objetos decorator los hace más propenso a errores que la herencia. Por ejemplo, es posible combinar objetos decorator de diferentes formas que no funcionen, o crear referencias circulares entre los objetos decorator.

8 comentarios:

Anónimo dijo...

Excelente explicación ! gracias !

Anónimo dijo...

... y bien explicada. Desearía que el ejemplo fuese con Componentes GUI . . .

Anónimo dijo...

Muy buena explicación. Me ha quedado perfectamente claro. El ejemplo ha sido muy bien escogido

Manu dijo...

Muchísimas gracias por esta explicación. Clara, concisa, estupenda. Me ha servido de gran ayuda.

Max dijo...

Muchas gracias, me alegro haber sido de ayuda :)

Unknown dijo...

Muchas gracias, probablemente su blog me salve de un suspenso estrepitoso. Pero durante la realización del código a la hora de aplicar los DecoradoresConcretos el programa no compila durante las líneas de llamar al superconstructor:

ej:

public AireAcondicionado (Vendible vendible){
super(vendible);
}

El código lo he copiado exactamente igual al suyo :/ ... ¿Dónde puede radicar el error?

Gracias por su tiempo.

Max dijo...

Hans necesito ver el codigo para entender un poco mejor el problema.

Anónimo dijo...

Gracias pa Messirve