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 y las relaciones existentes entre ellas

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.

Clase AbstractIcon

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.

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.

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.

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.

Form 1a Parte

Form 2a ParteSimplemente 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!