Decorator
O Decorator permite adicionar comportamentos a um objeto dinamicamente sem alterar sua classe original.
Imagine:
interface Coffee {
cost(): number;
}
Temos um café:
class SimpleCoffee
implements Coffee {
cost() {
return 5;
}
}
Agora queremos adicionar:
Leite
Chocolate
Açúcar
Uma abordagem seria criar várias subclasses:
Coffee
├── CoffeeWithMilk
├── CoffeeWithChocolate
├── CoffeeWithMilkAndChocolate
├── CoffeeWithMilkAndSugar
└── ...
Isso rapidamente se torna difícil de manter.
Com Decorator, podemos combinar comportamentos.
Decorator
class MilkDecorator
implements Coffee {
constructor(
private coffee: Coffee
) {}
cost() {
return this.coffee.cost() + 2;
}
}
Outro:
class ChocolateDecorator
implements Coffee {
constructor(
private coffee: Coffee
) {}
cost() {
return this.coffee.cost() + 3;
}
}
Agora podemos combinar:
let coffee: Coffee =
new SimpleCoffee();
coffee =
new MilkDecorator(
coffee
);
coffee =
new ChocolateDecorator(
coffee
);
O preço final:
console.log(
coffee.cost()
);
Será:
5 + 2 + 3 = 10
Visualmente
ChocolateDecorator
↓
MilkDecorator
↓
SimpleCoffee
Cada Decorator adiciona um comportamento.
Exemplo real
Um exemplo comum é adicionar funcionalidades a requisições HTTP:
Request
↓
LoggingDecorator
↓
AuthenticationDecorator
↓
CacheDecorator
↓
API
Cada camada pode adicionar uma responsabilidade.
Quando usar?
Decorator é útil quando:
- Queremos adicionar comportamentos dinamicamente.
- Existem muitas combinações de funcionalidades.
- Herança criaria muitas subclasses.
- Queremos seguir o princípio Open/Closed.
A ideia principal é:
Adicionar responsabilidades a um objeto sem modificar sua classe original.