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
Component
i representa l'objecte original al qual es poden afegir responsabilitats. - Decorator: Manté una referència a un objecte
Component
i 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