Padrões de Projetos - Decorator
Afluentes: Modelos, métodos e técnicas da engenharia de software
Descrição
O padrão Decorator é usado para adicionar comportamentos ou responsabilidades adicionais a um objeto dinamicamente, sem alterar sua estrutura básica. Ele é especialmente útil quando queremos adicionar funcionalidades de maneira flexível, sem precisar criar subclasses para cada combinação de funcionalidades possíveis.
Como Funciona o Decorator
- Componente: Interface ou classe abstrata que define a interface comum para os objetos que podem ter funcionalidades adicionadas.
- Componente Concreto: Implementação básica do componente que pode receber as funcionalidades adicionais.
- Decorator: Classe abstrata que implementa a interface
Componente
e armazena uma referência a um componente. Ela delega as chamadas para o componente, mas pode adicionar comportamento adicional. - Decorator Concreto: Implementa o comportamento adicional específico ao redor do componente.
Exemplo em Java
Vamos implementar um exemplo simples com uma interface Café
, onde temos um café básico e decoradores que adicionam funcionalidades como leite e açúcar.
Interface Cafe (Componente)
Define a interface comum para o objeto que será decorado, com os métodos descricao()
e preco()
.
// Interface Componente
interface Cafe {
String descricao();
double preco();
void mostrar();
}
Classe CafeSimples (Componente Concreto)
Implementa Cafe
e representa um café simples sem adições.
// Componente Concreto - Café simples
class CafeSimples implements Cafe {
@Override
public String descricao() {
return "Café simples";
}
@Override
public double preco() {
return 2.0;
}
@Override
public void mostrar() {
System.out.println(descricao() + " custa R$" + preco());
}
}
Classe CafeDecorator (Decorator)
É uma classe abstrata que implementa Cafe
e mantém uma referência a um objeto Cafe
. Ela delega as chamadas de descricao()
e preco()
para o objeto referenciado.
// Decorator abstrato - Implementa a interface Cafe e contém uma referência a um Cafe
abstract class CafeDecorator implements Cafe {
protected Cafe cafeDecorado;
public CafeDecorator(Cafe cafeDecorado) {
this.cafeDecorado = cafeDecorado;
}
@Override
public String descricao() {
return cafeDecorado.descricao();
}
@Override
public double preco() {
return cafeDecorado.preco();
}
@Override
public void mostrar() {
System.out.println(descricao() + " custa R$" + preco());
}
}
Classe LeiteDecorator (Decorator Concreto)
Decora o café com leite, adicionando comportamento e alterando a descrição e o preço.
// Decorator Concreto - Adiciona leite ao café
class LeiteDecorator extends CafeDecorator {
public LeiteDecorator(Cafe cafeDecorado) {
super(cafeDecorado);
}
@Override
public String descricao() {
return cafeDecorado.descricao() + ", com leite";
}
@Override
public double preco() {
return cafeDecorado.preco() + 0.5;
}
}
Classe AcucarDecorator (Decorator Concreto)
Decora o café com açúcar, adicionando comportamento e alterando a descrição e o preço.
// Decorator Concreto - Adiciona açúcar ao café
class AcucarDecorator extends CafeDecorator {
public AcucarDecorator(Cafe cafeDecorado) {
super(cafeDecorado);
}
@Override
public String descricao() {
return cafeDecorado.descricao() + ", com açúcar";
}
@Override
public double preco() {
return cafeDecorado.preco() + 0.2;
}
}
Classe Principal
// Classe principal para testar os Decorators
public class Main {
public static void main(String[] args) {
// Café simples
Cafe cafe = new CafeSimples();
cafe.mostrar();
// Café com leite
Cafe cafeComLeite = new LeiteDecorator(cafe);
cafeComLeite.mostrar();
// Café com leite e açúcar
Cafe cafeComLeiteEAcucar = new AcucarDecorator(cafeComLeite);
cafeComLeiteEAcucar.mostrar();
}
}
Saída Esperada
Café simples custa R$2.0
Café simples, com leite custa R$2.5
Café simples, com leite, com açúcar custa R$2.7
Vantagens do Decorator
- Flexibilidade: Adiciona funcionalidades aos objetos de forma dinâmica e flexível, sem criar subclasses para cada combinação de funcionalidades.
- Extensibilidade: Novos decorators podem ser criados e combinados sem alterar o código existente.
Desvantagens do Decorator
- Complexidade: Muitos decorators encadeados podem tornar o código mais difícil de entender e depurar.
- Muitos Objetos: Cada decorator cria um novo objeto, o que pode aumentar a sobrecarga de memória em casos complexos.
Resumo
O padrão Decorator é ideal para situações onde você precisa de funcionalidades adicionais que podem variar dinamicamente, como adicionar ingredientes em uma bebida ou características em uma interface de usuário.