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

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

challenge icon

Desafio

Fácil

Vamos 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 interface Notifier, que servirá como a interface de 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 concreta que implementa Notifier. Ela representa a forma mais simples de notificação: um alerta no aplicativo. Quando send for chamado, ele deverá imprimir In-App: [message].
  • NotifierDecorators.java: Crie aqui sua hierarquia de decorators. Comece com uma classe abstrata NotifierDecorator que implemente Notifier e mantenha uma referência para um objeto Notifier envolvido (passado por seu construtor).

    Em seguida, crie três decorators concretos que estendam NotifierDecorator:

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

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

    PushDecorator - Quando send for chamado, primeiro delega para o notificador envolvido e depois imprime Push: [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ão email, sms e push.

    Por fim, chame send com 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
        
    }
}
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

Pratique por conta própria: Compilador de Java online