Padrão Decorator
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy. Lição 72 de 87.
O padrão Decorator é um padrão de projeto estrutural que permite adicionar novos comportamentos a objetos envolvendo-os em objetos especiais chamados decoradores. Diferentemente da herança, que adiciona comportamento em tempo de compilação, os decoradores adicionam funcionalidades dinamicamente em tempo de execução, sem modificar a classe original.
Imagine uma cafeteria onde você começa com um café básico e pode adicionar extras como leite, açúcar ou chantilly. Cada adição envolve o café original, acrescentando seu custo e sua descrição. O padrão usa uma interface comum para que os objetos decorados possam ser tratados da mesma forma que o original:
interface Coffee {
String getDescription();
double getCost();
}
class SimpleCoffee implements Coffee {
public String getDescription() {
return "Simple Coffee";
}
public double getCost() {
return 2.0;
}
}
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
public String getDescription() {
return coffee.getDescription() + ", Milk";
}
public double getCost() {
return coffee.getCost() + 0.5;
}
}O decorador mantém uma referência ao objeto encapsulado e delega as chamadas a ele enquanto adiciona seu próprio comportamento. Você pode empilhar vários decoradores para combinar recursos:
Coffee order = new SimpleCoffee();
order = new MilkDecorator(order);
order = new SugarDecorator(order);
System.out.println(order.getDescription()); // Simple Coffee, Milk, Sugar
System.out.println(order.getCost()); // 2.75O Padrão Decorator se destaca quando você precisa adicionar responsabilidades a objetos sem criar uma explosão de subclasses. Cada decorator é independente, então você pode combiná-los livremente para criar exatamente a combinação de comportamentos de que precisa.
Desafio
FácilVamos criar um sistema de notificações usando o Padrão Decorator! Você criará uma notificação básica que pode ser aprimorada com diferentes canais de entrega — alertas por e-mail, alertas por SMS e notificações push — envolvendo decorators uns nos outros. Isso reflete sistemas do mundo real nos quais uma única mensagem pode precisar ser enviada por vários canais simultaneamente.
Você organizará seu código em quatro arquivos:
Notifier.java: Defina a interfaceNotifier, que servirá como a interface de componente para o seu padrão decorator. Ela deve declarar um métodosend(String message)que lida com o envio de notificações.BasicNotifier.java: Crie a classe de componente concreta que implementaNotifier. Ela representa a forma mais simples de notificação: um alerta no aplicativo. Quandosendfor chamado, ele deverá imprimirIn-App: [message].NotifierDecorators.java: Crie aqui sua hierarquia de decorators. Comece com uma classe abstrataNotifierDecoratorque implementeNotifiere mantenha uma referência para um objetoNotifierenvolvido (passado por seu construtor).Em seguida, crie três decorators concretos que estendam
NotifierDecorator:EmailDecorator- Quandosendfor chamado, primeiro delega para o notificador envolvido e depois imprimeEmail: [message].SMSDecorator- Quandosendfor chamado, primeiro delega para o notificador envolvido e depois imprimeSMS: [message].PushDecorator- Quandosendfor chamado, primeiro delega para o notificador envolvido e depois imprimePush: [message].Main.java: Reúna seu sistema de decorators! Você receberá duas entradas: a mensagem de notificação (String) e uma lista separada por vírgulas dos canais a serem adicionados (String), por exemplo,"email,sms"ou"push,email,sms".Comece com um
BasicNotifier. Em seguida, com base na entrada de canais, envolva-o com os decorators apropriados na ordem em que aparecem. Os nomes de canais válidos sãoemail,smsepush.Por fim, chame
sendcom sua mensagem no notificador totalmente decorado.
Você receberá duas entradas, nesta ordem: a mensagem a ser enviada (String) e os canais a serem habilitados (String, separados por vírgulas).
Por exemplo, com as entradas Server is down! e email,push, sua saída seria:
In-App: Server is down!
Email: Server is down!
Push: Server is down!Observe como cada decorator adiciona seu próprio comportamento enquanto preserva a notificação original. A ordem do envolvimento determina a ordem da saída: o componente mais interno (BasicNotifier) é executado primeiro, depois cada decorator adiciona sua camada. Você pode combinar livremente qualquer combinação de canais sem criar classes separadas para cada combinação!
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler a mensagem a enviar
String message = scanner.nextLine();
// Ler a lista de canais separados por vírgula (ex.: "email,sms" ou "push,email,sms")
String channelsInput = scanner.nextLine();
// TODO: Comece com um BasicNotifier
// TODO: Divida a entrada de canais por vírgula
// TODO: Percorra cada canal e envolva o notifier com o decorator apropriado
// Canais válidos são: "email", "sms", "push"
// TODO: Chame send no notifier totalmente decorado com a mensagem
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão IteratorPratique por conta própria: Compilador de Java online