Observer
Intenção (GoF)
Seção intitulada “Intenção (GoF)”Definir uma dependência um-para-muitos entre objetos, de modo que, quando um objeto muda de estado, todos os seus dependentes sejam notificados e atualizados automaticamente.
Problema
Seção intitulada “Problema”Vários objetos precisam reagir a mudanças no estado de outro, mas acoplar o objeto observado diretamente a cada interessado o amarra a eles e viola o OCP a cada novo dependente. Precisamos que o subject notifique um número variável e desconhecido de observers, sem conhecê-los concretamente.
Estrutura
Seção intitulada “Estrutura”- Subject (Observable): mantém a lista de observers e oferece
subscribe/unsubscribe; ao mudar, chamanotify. - Observer: interface com
update(...), implementada por cada interessado. - ConcreteObserver: reage à notificação, eventualmente consultando o subject.
Há duas variantes de entrega: push (o subject envia os dados no update) e pull (o observer consulta o subject após ser avisado).
Consequências e trade-offs
Seção intitulada “Consequências e trade-offs”Prós
- Baixo acoplamento: subject conhece apenas a interface
Observer. - Broadcast dinâmico; observers entram e saem em runtime.
- Novos observers sem alterar o subject (OCP).
Contras
- Cascatas de notificação difíceis de prever e depurar.
- Ordem de notificação geralmente indeterminada.
- Vazamento de memória (lapsed listener) se observers não se desinscrevem.
- Observer síncrono acopla o subject ao tempo de execução dos observers.
Quando NÃO usar
Seção intitulada “Quando NÃO usar”Quando há um único dependente conhecido e fixo (uma chamada direta é mais clara), ou quando a “reação” precisa de garantias transacionais/ordem estrita que o broadcast simples não oferece.
Exemplo em Java
Seção intitulada “Exemplo em Java”Antes: subject acoplado aos interessados
Seção intitulada “Antes: subject acoplado aos interessados”public class Order {
private final EmailService email = new EmailService(); private final InventoryService inventory = new InventoryService();
public void confirm() { this.status = "CONFIRMED"; email.sendConfirmation(this); inventory.reserveItems(this); }}Cada novo efeito colateral (fidelidade, analytics, nota fiscal) exige editar Order.
Depois: observers desacoplados
Seção intitulada “Depois: observers desacoplados”public interface OrderObserver { void onOrderConfirmed(Order order);}
public class Order {
private final List<OrderObserver> observers = new ArrayList<>(); private String status;
public void subscribe(OrderObserver observer) { observers.add(observer); }
public void unsubscribe(OrderObserver observer) { observers.remove(observer); }
public void confirm() { this.status = "CONFIRMED"; notifyObservers(); }
private void notifyObservers() { for (OrderObserver o : List.copyOf(observers)) { o.onOrderConfirmed(this); } }
public String status() { return status; }}Observers concretos:
public class EmailNotifier implements OrderObserver { public void onOrderConfirmed(Order order) { System.out.println("Enviando e-mail de confirmacao"); }}
public class InventoryReserver implements OrderObserver { public void onOrderConfirmed(Order order) { System.out.println("Reservando itens em estoque"); }}Uso:
Order order = new Order();order.subscribe(new EmailNotifier());order.subscribe(new InventoryReserver());order.confirm();Order não conhece mais os serviços concretos; adicionar reações é assinar novos observers.
Diagrama
Seção intitulada “Diagrama”sequenceDiagram
participant Client
participant Order as Order (Subject)
participant Email as EmailNotifier
participant Inv as InventoryReserver
Client->>Order: subscribe(email)
Client->>Order: subscribe(inventory)
Client->>Order: confirm()
Order->>Order: status = CONFIRMED
Order->>Email: onOrderConfirmed(order)
Order->>Inv: onOrderConfirmed(order)
Ponto-chave: base de Domain Events e listeners
Seção intitulada “Ponto-chave: base de Domain Events e listeners”O Observer é a semente conceitual dos Domain Events e dos listeners. Em vez de um subject notificar observers em memória, o domínio publica um evento (OrderConfirmed) e handlers registrados reagem. Vale distinguir dois regimes:
- Observer síncrono (in-process): a notificação roda na mesma thread e transação do subject. Simples e imediato, mas o subject fica acoplado ao tempo e às falhas dos observers — uma exceção num observer pode abortar a operação principal.
- Eventos assíncronos (mensageria/event bus): o evento é publicado e processado fora do fluxo original — outra thread, outro processo, outro serviço. Ganha-se desacoplamento temporal, resiliência e escala, ao custo de consistência eventual, necessidade de idempotência e infraestrutura (broker, outbox).
A estrutura mental — algo aconteceu, interessados reagem — é a mesma do Observer; muda o mecanismo de entrega e as garantias. No Domain-Driven Design, Agregados emitem Domain Events que outros Agregados ou Use Cases consomem, tipicamente de forma assíncrona para preservar a autonomia de cada agregado.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Lapsed listener / vazamento de memória: observers que nunca se desinscrevem mantêm o subject vivo e acumulam handlers. Gerencie o ciclo de vida (unsubscribe, referências fracas).
- Cascata de notificações e reentrância: um observer que altera o subject e dispara novas notificações, gerando laços difíceis de rastrear. Iterar sobre uma cópia da lista evita
ConcurrentModificationException. - Observer síncrono com trabalho pesado ou I/O, travando o fluxo principal — prefira eventos assíncronos nesses casos.
- Depender da ordem de notificação, que geralmente é indefinida.
- Tratar Observer como Mediator: se você precisa de lógica de coordenação entre partes, veja Mediator.
Relações
Seção intitulada “Relações”- Padrão comportamental de Padrões Comportamentais / Design Patterns (GoF).
- Base conceitual de Domain Events e listeners no Domain-Driven Design; emitido por Agregados.
- Contrasta com Mediator (descentralizado vs. centralizado).
- Apoia o OCP ao permitir novos observers sem alterar o subject.
- Índice: Design Patterns (GoF) · Catálogo de Patterns.
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Que tipo de dependência o Observer define e como o acoplamento é mantido baixo?
Resposta
Uma dependência um-para-muitos: quando o subject muda, todos os observers são notificados. O acoplamento fica baixo porque o subject conhece apenas a interface Observer, não os observers concretos — pode-se adicionar ou remover interessados sem alterá-lo.
2. Diferencie a variante push da pull.
Resposta
No push, o subject envia os dados relevantes já no update/notify. No pull, o subject apenas avisa que algo mudou e cada observer consulta o subject para obter o que precisa. Push é conveniente; pull dá mais autonomia ao observer e evita enviar dados desnecessários.
3. Como o Observer se relaciona com Domain Events e qual a diferença entre observer síncrono e evento assíncrono?
Resposta
Domain Events são a evolução do Observer: em vez de notificar observers em memória, o domínio publica um evento e handlers reagem. O observer síncrono roda na mesma thread/transação (imediato, mas acopla tempo e falhas). O evento assíncrono é processado fora do fluxo (desacoplamento temporal, resiliência, escala), ao custo de consistência eventual e idempotência.
4. Cite dois erros comuns e como evitá-los.
Resposta
(1) Lapsed listener/vazamento de memória: observers que não se desinscrevem — resolva gerenciando o ciclo de vida ou usando referências fracas. (2) ConcurrentModificationException e cascatas: um observer altera a lista ou o subject durante a notificação — itere sobre uma cópia da lista e evite reentrância descontrolada.