Iba a poner El patrón de diseño decorator en PHP pero dado que los patrones de diseño son independientes del lenguaje preferí poner un título más matizado. Aunque algunos parezcan empeñados en no usar patrones en PHP (a pesar de que desde PHP5 ya esté orientado a objetos) en un proyecto grande serán útiles.
El patrón Decorator se pensó para permitir herencia múltiple sin que el árbol de clases crezca de forma incontrolada. Permite añadir a un objeto funcionalidades de forma dinámica. Piensa en un juego de rol, piensa en cuando vinculabas una matriz de conjuros a un arma en Runequest… vale, es muy friki, pero lo voy a usar de ejemplo para explicar el patrón.
Imagina que haces un videojuego, inspirado en el antes citado Runequest mismamente. Entonces creas la clase espada, que hace 10 puntos de daño y tiene 20 puntos de armadura. Muy bien, ahora le vinculas un hechizo de armadura extra. Tendrías que crear la clase espada_armadura con 10 puntos más de armadura. Hasta ahí, bien: tienes la clase espada, y la clase hija espada_armadura que hereda de espada.
Ahora imagina que también le vinculas un hechizo que duplica el daño. Tendrías que crear la clase espada_daño, pero también la clase espada_armadura_daño, que sería la clase con ambos hechizos. Para dos funcionalidades has tenido que crear 3 clases hijas. Y ahora supón un tercer hechizo que mejora tu posibilidad de éxito un 100%: espada_éxito, espada_éxito_armadura, espada_éxito_daño, espada_éxito_daño_armadura… y las cuatro clases anteriores. Para tres funcionalidades ya vas en 8 clases, si haces el cálculo progresan en potencias de 2, por lo que para 4 funcionalidades tendrías 16 clases, para 5 32…
El patrón Decorator nos permite evitar esto a base de delegar funciones. Vamos a poner un ejemplo práctico en PHP solucionando el problema anterior, explicado en los comentarios:
//interfaz arma, común a todas las armas interface iArma{ public function ataca(); public function defiende(); } //clase espada, que implementa la interfaz arma class espada implements iArma{ private $armor = 20; private $attack = 10; public function ataca(){ return $this->attack; } public function defiende(){ return $this->armor; } } //creamos la clase abstracta Decorator abstract class espada_decorator implements iArma{ protected $_espada; public function __construct(iArma $Espada){ $this->_espada = $Espada; } } //ahora creamos la funcionalidad con más ataque class extra_attack extends espada_decorator{ public function ataca(){ return $this->_espada->ataca()*2; } } //ahora la funcionalidad de más defensa class extra_armor extends espada_decorator{ public function defiende(){ return $this->_espada->defiende()+10; } } //ahora podemos crear un objeto espada con la defensa extra $espada = new extra_armor(new espada()); //y ahora añadimos el ataque extra $espada = new extra_attack($espada);
Como puedes ver, de esta forma agregamos a nuestro objeto diversas funcionalidades, creando sólo una clase por funcionalidad en lugar del montón al que nos obligaría el ir creando clases para cara posible combinación.