Está en la página 1de 8

¿QUÉ ES UN PATRÓN DECORADOR?

Uno de los patrones estructurales más utilizados en el diseño de GUI (interfaz gráfica de usuario) es
el patrón Decorator. El patrón Decorator nos da una manera de usar la herencia como un
complemento condicional. ¿Qué significa esto? Significa que la herencia se usa en el patrón de tal
manera que cada clase heredada sea una suma de todas las partes.

El patrón Decorador tiene cuatro partes principales: Component, Concrete Component, Decorator,
y Concrete Decorator. El Component suele ser una clase abstracta que mantiene la funcionalidad
básica tanto para las clases no decoradas como para las decoradas. Por no decorado quiero decir
sin aplicar una clase de decorador al componente existente. El siguiente aspecto del patrón es el
Concrete Component. Esta es la clase de implementación no decorada del componente, que
podemos instanciar sin un decorador. La siguiente parte del patrón nos da el nombre del patrón. Se
compone de dos partes: el Decorator, y el Concrete Decorator. El Decorator es la clase abstracta que
hereda del componente y contiene una instancia encapsulada de nuestro componente concreto
deseado. El componente concreto es la clase de implementación con la funcionalidad adicional
deseada para nuestro decorador.

Después de leer la descripción anterior, es posible que todavía se esté preguntando cómo es útil el
patrón. Un uso es proporcionar herencia flexible. Podemos elegir a nuestro decorador para que sea
del tipo base de componente, lo que nos puede dar la misma funcionalidad básica que podríamos
tener en un control no decorado, pero nos permite agregar la funcionalidad deseada de manera ad
hoc. Al hacer que un control sea un decorador, agregamos todas las funciones de control y algunas
funciones nuevas deseadas o, si no queremos tener la nueva funcionalidad, simplemente podemos
usar el control no decorado. Esta capacidad de intercambio nos da una mayor flexibilidad al tratar
con clases heredadas.
PROBLEMA: DESEAMOS CIERTAS FUNCIONES ASOCIADAS CON UN TIPO DE CLASE SÓLO CUANDO
SE DESEA, PERO NO TODO EL TIEMPO

Nuestro problema funcional implica un control de interfaz de usuario común heredado de una clase
base abstracta. En ciertos momentos nos gustaría agregar funcionalidad a la clase, en este caso, la
lógica de la barra de desplazamiento, pero hay ocasiones en las que agregar esta lógica es
inmanejable o inadecuado.

Comenzamos mirando nuestra clase base abstracta llamada WindowControl. Notamos que contiene
muchas propiedades genéricas de tamaño y posicionamiento de control, como podríamos esperar
de un control gráfico base. El control también contiene un método abstracto llamado Render(), que
podemos asumir utiliza las otras propiedades para hacer que el control sea visible.
Ya tenemos esta clase en nuestro código y está funcionando correctamente. También queremos
echar un vistazo a la clase de implementación concreta específica a la que deseamos agregar un
decorador. La clase TextBox hereda nuestra base abstracta y anula el método Render() para realizar
el trabajo real de representación de los aspectos gráficos del control. Toma en su constructor las
propiedades necesarias para realizar esta funcionalidad. También alberga otra variable de instancia,
que no tiene ningún efecto en la representación del control.

Cuando ejecutamos el código, representa la clase TextBox como se esperaba. Pero hay veces en que
nuestro cuadro de texto necesita que se le agregue alguna lógica de barra de desplazamiento:
Podríamos simplemente agregar esta lógica a la clase TextBox, pero podría ser confusa o causar
problemas al usar un cuadro de texto no desplazable. Además, como no queremos mostrar la barra
de desplazamiento en todo momento con el cuadro de texto, no sería recomendable agregar esta
funcionalidad a la clase. En resumen, no es apropiado agregar las funciones de desplazamiento
directamente al cuadro de texto.

Otra forma en que podríamos lograr esto es simplemente heredar de la clase TextBox y crear una
clase de TextBox desplazable. Esto no es difícil en implementaciones simples, pero a medida que
aumenta la complejidad en las bases y en toda la cadena de herencia, esto puede ser problemático.

¿Cómo afrontamos este problema? Veamos nuestra sección de soluciones para ver.

SOLUCIÓN: PERMITAMOS A UN DECORADOR QUE HEREDE DE LA CLASE QUE ESTAMOS


UTILIZANDO PARA TOMAR SU LUGAR Y ACTUAR COMO LA CLASE, PERO CON LA MAYOR
FUNCIONALIDAD DEL DECORADOR

Cuando observamos nuestro problema, fácilmente podríamos decir que podríamos agregar lógica
booleana (if...then...else) al método de render para verificar los valores de desplazamiento y
renderizar solo si los valores se establecieron:
Pero eso realmente no haría uso de los principios OOP en nuestro código ni sería intuitivo al realizar
cambios. No podemos saber rápidamente mediante este código si tenemos o no un cuadro de texto
desplazable. Tenemos que esperar hasta el tiempo de ejecución y luego tratar de ver si hay valores
válidos presentes. Esto puede ser tanto confuso en la intención del objeto como en violación de las
reglas de encapsulación: nuestra clase solo debe saber sobre las barras de desplazamiento si
realmente tiene una.

Así que necesitamos otra forma de lidiar con esta situación. Un patrón de decorador parece ser el
camino a seguir. Usando un decorador podemos intercambiar nuestros objetos. Podemos usar la
clase decorada cuando necesitamos una barra de desplazamiento y nuestra clase TextBox cuando
no la necesitamos. Comencemos el esfuerzo de refactorización en el punto de creación de nuestro
decorador.

Como vimos en nuestra sección de problemas, ya tenemos el componente y el componente


concreto. Para este esfuerzo de refactorización, no deseamos cambiar ninguna de las clases. La
verdadera fuerza del patrón Decorador se aclara en esto. Como no tenemos que modificar la
funcionalidad existente, podemos comenzar fácilmente con los nuevos aspectos del código que
deseamos adquirir.
Comenzamos con nuestra clase de decorador abstracto. Siguiendo el patrón, heredamos esta clase
del componente abstracto que ya tenemos: WindowControl. Hacemos esto para proporcionar la
funcionalidad básica que podríamos obtener con la clase TextBox. A continuación, debemos
proporcionar una instancia protegida de nuestro componente. Hacemos esto para que se pueda
pasar una instancia concreta y podamos aprovechar esa instancia en nuestro decorador. Lo que
queremos es capturar toda la funcionalidad de nuestro componente concreto, al mismo tiempo que
lo encapsulamos como una variable de instancia. Observe que también anulamos el método
abstracto Render() y llamamos al método Render() de nuestra instancia protegida. La instancia está
protegida para que el decorador de concreto heredado pueda manipularlo directamente.

A continuación, creamos nuestro decorador de hormigón. En esta clase es donde podemos agregar
y cambiar directamente la funcionalidad que obtenemos con nuestra clase TextBox. En general,
queremos agregar nuestra nueva funcionalidad de lógica de barra de desplazamiento a la
funcionalidad de TextBox existente. Heredamos de la clase base abstracto decorator y
proporcionamos nuestras variables de instancia para la configuración de la barra de
desplazamiento:

El constructor de la clase toma como parámetro de entrada el tipo de objeto TextBox. Esto se hace
para proporcionar una manera más intuitiva de determinar que estamos decorando una clase
TextBox:
A continuación, agregamos nuestras propiedades para configurar la barra de desplazamiento:

El último paso es anular el método Render() y agregarle la lógica de representación de la barra de


desplazamiento. Note que primero llamamos a nuestro método base. Hacemos esto porque en la
representación del control a la interfaz de usuario, el componente del cuadro de texto base debe
representarse delante de la barra de desplazamiento. Esto se puede cambiar caso por caso.

Ahora tenemos todas las piezas necesarias para utilizar nuestro decorador. Necesitamos ver cómo
podemos usar el decorador y el cuadro de texto como piezas intercambiables. En nuestro código
necesitamos un cuadro de texto básico. Podemos crear una instancia de esto, establecer sus
propiedades y llamar a su método Render():

A continuación, en alguna otra parte de nuestro código, debemos hacer que nuestro cuadro de texto
sea desplazable. Necesitamos usar un tipo que herede del componente, como lo hace nuestro
decorador. Alimentamos nuestra instancia de cuadro de texto anterior al decorador, configuramos
los componentes desplazables y llamamos a su método Render(). El método de nuestro decorador
llama al método de la instancia de cuadro de texto encapsulado, y luego le agrega su lógica de render
decorada, lo que le da un efecto compuesto.
La implementación del decorador ahora nos brinda una forma intuitiva, fácilmente reconocible e
intercambiable para tratar de agregar funcionalidad, sin utilizar cadenas directas de herencia.
Minimizamos la confusión al disminuir los niveles de herencia entre la clase TextBox y cualquier
clase heredada que de otro modo podríamos crear.

COMPARACIÓN CON PATRONES SIMILARES

El patrón Decorator es fácilmente comparable con otros patrones. Uno de esos patrones es el patrón
Adaptador. Este patrón también oculta la funcionalidad de otra clase y permite solo el acceso
esperado a la clase interna. Los decoradores a menudo trabajan bien para aumentar la flexibilidad
de los Composites, donde las relaciones de clase se extienden a través de una colección y se pueden
llamar métodos entre varias clases para crear un todo compuesto.

LO QUE HEMOS APRENDIDO

Los decoradores nos dan una forma más flexible de lidiar con la herencia de clase. Nos permiten
ampliar e intercambiar tipos de clases y funcionalidades al permitirnos agregar nuevas
funcionalidades a una clase existente de una manera más fácil de reconocer y mantener. Nos da un
control más explícito de la funcionalidad de una clase, sin aumentar la complejidad del código
cuando se usan niveles de clases heredadas.

PATRONES RELACIONADOS

 Adapter pattern (Patrón adaptador)


 Composite pattern (Patrón compuesto)
 Visitor pattern (Patrón de visitante)

También podría gustarte