En la programació orientada a objectes, el patró Decorator (alternativament conegut com a Wrapper, un nom amb què també és conegut el patró Adaptador) és un patró de disseny que permet afegir un comportament a un objecte, tant estàticament com dinàmicament, sense que la resta d'objectes de la mateixa classe vegin alterat el seu comportament [1] El patró Decorator és útil sovint per complir el principi de responsabilitat única, car permet dividir una funcionalitat en diverses classes amb una única àrea d'afectació.[2]
El patró Decorator s'utilitza per estendre (decorar) la funcionalitat d'un cert objecte de forma estàtica, o en alguns casos en temps d'execució, independentment de la resta d'instàncies de la mateixa classe, en el cas que hagi estat dissenyat prèviament. Aquest comportament s'aconsegueix dissenyant una classe decorator nova que encapsuli la classe original. Aquest encapsulament es podria aconseguir seguint els següents passos:
Aquest patró està dissenyat de manera que múltiples decoradors poden ser apilats un sobre l'altre, cada cop afegint-hi una nova funcionalitat al(s) mètode(s) sobreescrit(s).
S'entén que els decoradors i l'objecte de la classe original compartiran un conjunt de característiques comunes. En l'esquema anterior, el mètode "operation()" està disponible tant en la versió decorada com en la no decorada.
Les característiques del Decorator (per exemple, mètodes, propietats, o d'altres membres) estan definides normalment per un interfície, per mixin o per l'herència de classes que és compartida pels decorators i l'objecte decorat. En l'exemple anterior, la classe "Component" és heretada tant per "ConcreteComponent" com per la subclasse que descendeix de "Decorador".
El patró Decorator és una alternativa a l'herència. L'herència afegeix comportament en temps de compilació i el canvi afecta a totes les instàncies de la classe original; fet que difereix del Decorator que proporciona un nou comportament en temps d'execució en objectes concrets.
El següent exemple Java ambientat en la preparació de cafè il·lustra l'ús de decoradors. En aquest exemple només es tenen en compte costos i ingredients.
// La classe abstracta Cafe defineix la funcionalitat de Cafe implementada pel decorador
public abstract class Cafe {
public abstract double getCost(); // Retorna el cost del cafè
public abstract String getIngredients(); // Retorna els ingredients del cafè
}
// Extensió d'un simple "Cafe" sense cap ingredient extra
public class CafeSimple extends Cafe {
public double getCost() {
return 1;
}
public String getIngredients() {
return "Cafè";
}
}
La següent classe conté els decoradors per totes les classes Cafè, heretant les classes decorador pròpies.
// Classe abstracte Decorator - fixi-se que estén de la classe abstracta Cafe
public abstract class CafeDecorator extends Cafe {
protected final Cafe CafeDecorator;
public CafeDecorator(Cafe c) {
this.cafeDecorat = c;
}
public double getCost() { // Implementació dels mètodes de la classe abstracta
return cafeDecorat.getCost();
}
public String getIngredients() {
return cafeDecorat.getIngredients();
}
}
// Decorator AmbLlet barreja llet amb cafè.
// Fixi's que estén de CafeDecorator.
class AmbLlet extends CafeDecorator {
public AmbLlet(Cafe c) {
super(c);
}
public double getCost() { // Substitució de mètode definit en la [[superclasse]] abstracta
return super.getCost() + 0.5;
}
public String getIngredients() {
return super.getIngredients() + ", Llet";
}
}
// Decorator AmbEncenallsXocolata barreja encenalls de xocolata amb el cafè.
// Fixi-se que estén CafeDecorator.
class AmbEncenallsXocolata extends CafeDecorator {
public AmbEncenallsXocolata(Cafe c) {
super(c);
}
public double getCost() {
return super.getCost() + 0.2;
}
public String getIngredients() {
return super.getIngredients() + ", Encenalls de xocolata";
}
}
El següent tros de codi és el programa que crea una instància Cafe que està totalment decorada (amb llet i encenalls de xocolata), i calcula el cost del cafè i mostra els seus ingredients:
public class Main {
public static void printInfo(Cafe c) {
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
}
public static void main(String[] args) {
Cafe c = new CafeSimple();
printInfo(c);
c = new AmbLlet(c);
printInfo(c);
c = new AmbEncenallsXocolata(c);
printInfo(c);
}
}
The output of this program is given below:
Cost: 1.0; Ingredients: Cafè Cost: 1.5; Ingredients: Cafè, Llet Cost: 1.7; Ingredients: Cafè, Llet, Encenalls de xocolata