Introducció al Patró Decorator
El patró Decorator és un patró estructural que permet afegir funcionalitats addicionals a un objecte de manera dinàmica. Aquest patró és especialment útil quan es volen afegir responsabilitats a objectes individuals de manera flexible i sense afectar altres objectes de la mateixa classe.
Objectius del Patró Decorator
- Flexibilitat: Permet afegir funcionalitats a objectes de manera dinàmica.
- Reutilització: Facilita la reutilització de codi, ja que les funcionalitats addicionals es poden encapsular en decoradors reutilitzables.
- Simplicitat: Evita la creació de subclasses per cada combinació possible de funcionalitats.
Components del Patró Decorator
- Component: Defineix la interfície per als objectes que poden tenir responsabilitats afegides dinàmicament.
- ConcreteComponent: Implementa la interfície
Componenti representa l'objecte original al qual es poden afegir responsabilitats. - Decorator: Manté una referència a un objecte
Componenti defineix una interfície que segueix la interfície delComponent. - ConcreteDecorator: Afegeix responsabilitats addicionals al
Component.
Diagrama UML del Patró Decorator
+-------------------+
| Component |
+-------------------+
| +operation():void |
+-------------------+
^
|
+-------------------+
| ConcreteComponent |
+-------------------+
| +operation():void |
+-------------------+
^
|
+-------------------+
| Decorator |
+-------------------+
| -component:Component |
| +operation():void |
+-------------------+
^
|
+-------------------+
| ConcreteDecorator |
+-------------------+
| +operation():void |
+-------------------+Exemple Pràctic
Escenari
Suposem que estem desenvolupant una aplicació de cafè on els clients poden personalitzar les seves begudes amb diferents addicions com llet, sucre, etc. Utilitzarem el patró Decorator per afegir aquestes funcionalitats de manera dinàmica.
Implementació en Java
Component
ConcreteComponent
// ConcreteComponent
public class Espresso implements Beverage {
@Override
public String getDescription() {
return "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}Decorator
// Decorator
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription();
}
@Override
public double cost() {
return beverage.cost();
}
}ConcreteDecorator
// ConcreteDecorator
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Milk";
}
@Override
public double cost() {
return beverage.cost() + 0.50;
}
}
public class Sugar extends CondimentDecorator {
public Sugar(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Sugar";
}
@Override
public double cost() {
return beverage.cost() + 0.20;
}
}Client
public class CoffeeShop {
public static void main(String[] args) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
beverage = new Milk(beverage);
System.out.println(beverage.getDescription() + " $" + beverage.cost());
beverage = new Sugar(beverage);
System.out.println(beverage.getDescription() + " $" + beverage.cost());
}
}Sortida Esperada
Exercicis Pràctics
Exercici 1
Implementa un nou decorador anomenat Mocha que afegeixi la descripció "Mocha" i un cost addicional de $0.75 a una beguda.
Solució
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return beverage.cost() + 0.75;
}
}Exercici 2
Crea una nova classe Latte que sigui un ConcreteComponent i tingui un cost de $2.50.
Solució
public class Latte implements Beverage {
@Override
public String getDescription() {
return "Latte";
}
@Override
public double cost() {
return 2.50;
}
}Errors Comuns i Consells
- No oblidar cridar al mètode del component original: Quan s'implementa un decorador, és important assegurar-se que es crida al mètode del component original per mantenir la funcionalitat existent.
- Evitar la creació de subclasses innecessàries: El patró Decorator permet afegir funcionalitats sense necessitat de crear subclasses per cada combinació possible de funcionalitats.
Conclusió
El patró Decorator és una eina poderosa per afegir funcionalitats a objectes de manera dinàmica i flexible. En lloc de crear subclasses per cada combinació de funcionalitats, podem utilitzar decoradors per afegir responsabilitats de manera modular i reutilitzable. Aquest patró és especialment útil en aplicacions on les funcionalitats poden variar de manera dinàmica i es necessiten solucions flexibles i escalables.
Curs de Patrons de Disseny de Programari
Mòdul 1: Introducció als Patrons de Disseny
- Què són els Patrons de Disseny?
- Història i Origen dels Patrons de Disseny
- Classificació dels Patrons de Disseny
- Avantatges i Desavantatges d'Usar Patrons de Disseny
Mòdul 2: Patrons Creacionals
Mòdul 3: Patrons Estructurals
Mòdul 4: Patrons de Comportament
- Introducció als Patrons de Comportament
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Mòdul 5: Aplicació de Patrons de Disseny
- Com Seleccionar el Patró Adequat
- Exemples Pràctics d'Ús de Patrons
- Patrons de Disseny en Projectes Reals
- Refactorització Usant Patrons de Disseny
Mòdul 6: Patrons de Disseny Avançats
- Patrons de Disseny en Arquitectures Modernes
- Patrons de Disseny en Microserveis
- Patrons de Disseny en Sistemes Distribuïts
- Patrons de Disseny en Desenvolupament Àgil
