Decorator pattern
El patrón decorador entra dentro de la categoría de patrones estructurales, y lo utilizamos cuando queremos añadir nueva funcionalidad a las instancias de una clase de una manera flexible, y a la vez cumplir el Principio de Responsabilidad Única (SRP).
Motivación para el patrón decorador
Problema 1: las limitaciones de la herencia
Existe una máxima en OOP:
Favor composition over inheritance
Es decir, que en muchos casos, es más conveniente utilizar patrones basados en composición que patrones basados en la herencia. Pero, ¿por qué? ¿qué limitaciones tiene la herencia?
Imaginemos que estamos trabajando en un proyecto en el que tenemos que crear un servicio para almacenar datos:
Hemos creado nuestro servicio y nos comunican que, además, algunos de los datos van a tener que almacenarse encriptados. Podríamos crear un nuevo servicio que extienda del anterior y sobreescribir el método para lograr nuestro objetivo:
Ya acabamos nuestro trabajo pero entra un nuevo requisito: algunos datos van a tener que comprimirse. Podríamos crear otro servicio:
¿Y si quiero que se guarden comprimidos y encriptados? Tal y como lo hemos implementado, solo podemos encriptar o comprimir, pero no las dos a la vez. Tendría que crear una clase CompressedAndEncryptedstorageService😖.
En este caso, utilizar herencia nos está causando varios problemas:
- Por cada requisito nuevo, tengo que implementar todas las posibles combinaciones. El código es poco flexible.
- Esto también hace que acabe repitiendo código, rompiendo con el principio DRY.
- A medida que vayan introduciéndose nuevos requisitos, va a ser mucho más difícil de mantener.
Problema 2: Cross-cutting concerns y principio de responsabilidad única
Otro de los requisitos que nos pueden pedir a la hora de implementar nuestro servicio de almacenamiento es que solo pueda ser utilizado por un usuario con el rol de admin (verificar la autorización), o que se pueda hacer logging.
Get Nazaret Miranda’s stories in your inbox
Join Medium for free to get updates from this writer.
La manera más directa podría ser similar a esta:
Esta implementación tiene varios problemas:
- No cumple el principio de responsabilidad única (SRP) en dos sentidos: (1) el método
writedebería tener la única responsabilidad de guardar los datos. Sin embargo, aquí está guardando datos, logueando y comprobando la autorización. (2) Estamos mezclando lógica de negocio con requisitos transversales (cross-cutting concerns), que no están relacionados con esa lógica. Esto “ensucia” el código haciendo que sea difícil de leer. - No es flexible: cada vez que introduzco o modifico un cross-cuting concern, modifico toda la clase. Si modifico la clase, el comportamiento se va a añadir a todos los objetos. Así que si quiero un `StorageService ` que no haga logging, tendría que crear una nueva clase.
Implementar el patrón decorador
El patrón decorador nos va a ayudar a solucionar los problemas de los casos anteriores, porque nos permite:
- Añadir funcionalidad a un objeto sin modificar la clase original, poniendo la nueva funcionalidad en su propia clase → cumplimos el SRP y reducimos el acoplamiento.
- Componer la funcionalidad a medida que necesitemos, a modo de capas → aportamos flexibilidad y permitimos la composición.
Pasos para implementar el patrón
- Cada nueva funcionalidad estará en una nueva clase, la clase decoradora.
- La clase decoradora va a extender de la clase original, y va a recibir por el constructor un objeto de la clase que extiende, el objeto decorado. Si no fuera así, no podríamos componer las funcionalidades.
- La nueva funcionalidad hace su operación, y luego delega en el método del objeto decorado.
Ejemplo:
Y procedemos de manera smilar para la autorización y el logging con AuthorizedStorageDecorator y LoggedStorageDecorator:
Ahora podemos almacenar datos “en crudo”, encriptados, comprimidos, con loggin o comprobando la autorización. Además como cada decorador extiende de StorageService, podemos anidar los constructores y crear todas las combinaciones que queramos:
Saber más
El patrón decorador es unos de los patrones en los que se basa el paradigma AOP (Programación Orientada a Aspectos, Aspect Oriented Programming), y puede usarse en combinación con otros patrones. Este artículo fue una introducción con ejemplos simplificados. Si quieres profundizar más, te dejo aquí las fuentes:
✨Fuentes:
- https://www.youtube.com/watch?v=Dqx_zWgpB4E
- https://sourcemaking.com/design_patterns/decorator
- https://therealmofcode.com/posts/2012/09/managing-cross-cutting-concerns-using-decorator-desing-pattern.html
- https://codewithmosh.com/courses/760032/lectures/13778915
- https://refactoring.guru/design-patterns/decorator






