Menu
Coddy logo textTech

Padrão Decorator

Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 72 de 87.

O Decorator Pattern é um padrão de projeto estrutural que permite acoplar novos comportamentos a objetos ao envolvê-los em objetos especiais chamados decoradores. Ao contrário da herança, que adiciona comportamento em tempo de compilação, os decoradores adicionam funcionalidade 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, adicionando seu custo e 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 envolvido e delega chamadas a ele, enquanto adiciona seu próprio comportamento. Você pode empilhar múltiplos decoradores para combinar funcionalidades:

Coffee order = new SimpleCoffee();
order = new MilkDecorator(order);
order = new SugarDecorator(order);

System.out.println(order.getDescription());  // Café Simples, Leite, Açúcar
System.out.println(order.getCost());         // 2.75

O Padrão Decorator brilha quando você precisa adicionar responsabilidades a objetos sem criar uma explosão de subclasses. Cada decorador é independente, então você pode misturá-los e combiná-los livremente para criar exatamente a combinação de comportamentos que você precisa.

challenge icon

Desafio

Fácil

Vamos construir um sistema de notificação usando o Decorator Pattern! Você criará uma notificação base que pode ser aprimorada com diferentes canais de entrega—alertas por e-mail, alertas por SMS e notificações push—envolvendo decoradores uns nos outros. Isso reflete sistemas do mundo real onde uma única mensagem pode precisar ser enviada através de múltiplos canais simultaneamente.

Você organizará seu código em quatro arquivos:

  • Notifier.java: Defina a interface Notifier que serve como a interface do componente para o seu padrão decorator. Ela deve declarar um método send(String message) que lida com o envio de notificações.
  • BasicNotifier.java: Crie a classe de componente concreto que implementa Notifier. Isso representa a forma mais simples de notificação—um alerta in-app. Quando send for chamado, ele deve imprimir In-App: [message].
  • NotifierDecorators.java: Construa sua hierarquia de decoradores aqui. Comece com uma classe abstrata NotifierDecorator que implementa Notifier e mantém uma referência a um objeto Notifier envolvido (passado através de seu construtor).

    Em seguida, crie três decoradores concretos que estendem NotifierDecorator:

    EmailDecorator - Quando send for chamado, ele primeiro delega para o notifier envolvido, depois imprime Email: [message].

    SMSDecorator - Quando send for chamado, ele primeiro delega para o notifier envolvido, depois imprime SMS: [message].

    PushDecorator - Quando send for chamado, ele primeiro delega para o notifier envolvido, depois imprime Push: [message].

  • Main.java: Reúna seu sistema de decoradores! Você receberá duas entradas: a mensagem de notificação (String) e uma lista de canais separada por vírgulas para adicionar (String), por exemplo "email,sms" ou "push,email,sms".

    Comece com um BasicNotifier. Então, com base na entrada de canais, envolva-o com os decoradores apropriados na ordem em que aparecem. Os nomes de canais válidos são email, sms e push.

    Finalmente, chame send com sua mensagem no notifier totalmente decorado.

Você receberá duas entradas em ordem: a mensagem a ser enviada (String) e os canais a serem ativados (String, separados por vírgula).

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 decorador adiciona seu próprio comportamento enquanto preserva a notificação original. A ordem de envolvimento determina a ordem da saída—o componente mais interno (BasicNotifier) executa primeiro, depois cada decorador adiciona sua camada. Você pode misturar e combinar qualquer combinação de canais sem criar classes separadas para cada combinação!

Folha de consulta

O Decorator Pattern (Padrão Decorador) é um padrão de projeto estrutural que anexa novos comportamentos a objetos ao envolvê-los em objetos decoradores. Ele adiciona funcionalidade dinamicamente em tempo de execução sem modificar a classe original.

O padrão usa uma interface comum para que os objetos decorados possam ser tratados da mesma forma que o objeto original.

Estrutura Básica

Defina uma interface de componente:

interface Coffee {
    String getDescription();
    double getCost();
}

Crie um componente concreto que implemente a interface:

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }
    public double getCost() {
        return 2.0;
    }
}

Crie uma classe decoradora abstrata que implemente a mesma interface e mantenha uma referência ao objeto envolvido:

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

Crie decoradores concretos que estendam o decorador abstrato:

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;
    }
}

Usando Decoradores

Empilhe múltiplos decoradores para combinar funcionalidades:

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.75

O decorador delega chamadas ao objeto envolvido enquanto adiciona seu próprio comportamento. Cada decorador é independente e pode ser misturado e combinado livremente.

Experimente você mesmo

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Lê a mensagem a ser enviada
        String message = scanner.nextLine();
        
        // Lê a lista de canais separada por vírgulas (ex: "email,sms" ou "push,email,sms")
        String channelsInput = scanner.nextLine();
        
        // TODO: Começar com um BasicNotifier
        
        // TODO: Dividir a entrada de canais por vírgula
        
        // TODO: Percorrer cada canal e envolver o notifier com o decorador apropriado
        // Canais válidos são: "email", "sms", "push"
        
        // TODO: Chamar send no notifier totalmente decorado com a mensagem
        
    }
}
quiz iconTeste seus conhecimentos

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