AULA 17 MÓDULO 5 design patterns ⏱ 60 min

Padrão Observer e Decorator

Observer: notificação desacoplada via interface. Decorator: responsabilidades dinâmicas sem herança. Observer vs Pub/Sub: quando usar cada um.

ObserverDecoratorinterfacecomposiçãoin-processPub/SubList

Padrão Observer — notificação desacoplada

Quando um pedido é confirmado, o sistema precisa: decrementar estoque, enviar email, registrar no analytics. Quem deve ter esse conhecimento? Se for a classe Pedido, ela precisaria importar EmailService, EstoqueService, AnalyticsService — acoplamento máximo.

💥
o problema sem Observer
Pedido.confirmar() com 50 linhas chamando serviços diferentes. Adicionar notificação no WhatsApp = modificar Pedido. Pedido se torna um deus que conhece tudo.
«interface» Observador + atualizar(Evento e): void ↑ implements ObsEstoque ObsEmail ObsAnalytics Pedido (sujeito observável) - observadores: List<Observador> + adicionar(Observador o): void + notificarTodos(Evento e): void

Padrão Decorator — responsabilidades dinâmicas

Decorator adiciona responsabilidades a um objeto em runtime sem herança. Em vez de criar subclasses para cada combinação, você empilha decoradores como camadas.

o exemplo clássico
Café R$2,00. + Leite: R$2,50. + Açúcar: R$2,70. + Canela: R$3,20. Com herança: CaféComLeite, CaféComLeiteMaisAçúcar, etc — explosão combinatória. Com Decorator: new Canela(new Açúcar(new Leite(new CaféSimples()))).

Observer vs Pub/Sub — diferença

Observer e Pub/Sub resolvem o mesmo problema de formas diferentes.

AspectoObserverPub/Sub
AcoplamentoIndireto (via interface)Total — via broker
EscopoIn-process (mesmo processo)Cross-process (sistemas distribuídos)
TecnologiasJava nativo, EventEmitterKafka, RabbitMQ, Redis Pub/Sub
PersistênciaNãoSim (mensagens em fila)
java
// Padrão Observer

public interface Observador {
    void atualizar(String evento, Object dados);
}

public class ObservadorEstoque implements Observador {
    public void atualizar(String evento, Object dados) {
        if ("PEDIDO_CONFIRMADO".equals(evento))
            System.out.println("[Estoque] decrementando: " + dados);
    }
}

public class ObservadorEmail implements Observador {
    public void atualizar(String evento, Object dados) {
        if ("PEDIDO_CONFIRMADO".equals(evento))
            System.out.println("[Email] enviando confirmação para: " + dados);
    }
}

// Sujeito observável
public class Pedido {
    private final List<Observador> observadores = new ArrayList<>();

    public void adicionarObservador(Observador o) { observadores.add(o); }
    public void removerObservador(Observador o) { observadores.remove(o); }

    private void notificarTodos(String evento, Object dados) {
        observadores.forEach(o -> o.atualizar(evento, dados));
    }

    public void confirmar() {
        // Pedido SÓ faz o que é dele — não sabe quem observa
        notificarTodos("PEDIDO_CONFIRMADO", this);
    }
}

// Padrão Decorator — Café
public interface Cafe {
    double getPreco();
    String getDescricao();
}

public class CafeSimples implements Cafe {
    public double getPreco() { return 2.0; }
    public String getDescricao() { return "Café"; }
}

public abstract class DecoradorCafe implements Cafe {
    protected Cafe base;
    DecoradorCafe(Cafe c) { this.base = c; }
}

public class Leite extends DecoradorCafe {
    Leite(Cafe c) { super(c); }
    public double getPreco() { return base.getPreco() + 0.50; }
    public String getDescricao() { return base.getDescricao() + ", Leite"; }
}

// Uso — empilhar decoradores
Cafe pedido = new Leite(new CafeSimples());
// pedido.getPreco() → 2.50, getDescricao() → "Café, Leite"
quiz · aula 17
Teste seus conhecimentos em Java
0/3 respondidas
QUESTÃO 01
No padrão Observer, o sujeito (Pedido) precisa conhecer as classes concretas dos observadores?
QUESTÃO 02
Por que Decorator usa composição em vez de herança para adicionar comportamento?
QUESTÃO 03
Qual diferença principal entre Observer e Pub/Sub?
0/3