Pular para o conteúdo
Comportamentais

Observer

Define uma dependência um-para-muitos entre objetos, de forma que quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente.

Intenção

Estabelecer um mecanismo de assinatura que permite que múltiplos objetos escutem e reajam a eventos ou mudanças de estado que ocorrem em outro objeto, sem acoplar fortemente o publicador aos seus assinantes.

Problema

Você tem um objeto cujas mudanças de estado são relevantes para outros objetos, mas não quer fixar essas dependências no código. Fazer polling em busca de mudanças é desperdício, e chamadas diretas de método criam acoplamento rígido entre o sujeito e cada parte interessada.

Solução

Defina um Subject que mantém uma lista de Observers e fornece métodos para anexar, desanexar, e notificá-los. Quando o estado do Subject muda, ele percorre a lista e chama um método de atualização em cada Observer. Observers se registram no Subject e implementam uma interface comum, então o Subject não precisa conhecer seus tipos concretos.

Participantes

  • Subject — mantém uma lista de observers e envia notificações na mudança de estado
  • Observer — define uma interface de atualização para objetos que devem ser notificados
  • ConcreteSubject — armazena o estado de interesse e dispara notificação quando o estado muda
  • ConcreteObserver — implementa a interface de atualização para manter seu estado consistente com o sujeito

Vantagens

  • Acoplamento fraco entre o sujeito e seus observers — o sujeito conhece apenas a interface do observer
  • Suporte a comunicação broadcast — qualquer número de observers pode se inscrever
  • Observers podem ser adicionados ou removidos em tempo de execução sem modificar o sujeito
  • Incentiva uma separação limpa entre o modelo de domínio principal e as camadas de apresentação ou efeito colateral

Desvantagens

  • A ordem de notificação não é garantida, o que pode causar bugs sutis se observers dependem uns dos outros
  • Vazamentos de memória podem ocorrer se observers não forem devidamente desanexados (problema do 'lapsed listener')
  • Atualizações em cascata podem ser caras ou causar efeitos colaterais inesperados quando observers disparam novas notificações
  • A depuração pode ficar mais difícil porque o fluxo de controle é implícito em vez de explícito

Analogia do mundo real

Um serviço de assinatura de jornal funciona exatamente como o padrão Observer. O editor (Subject) não precisa saber quem são os assinantes (Observers) ou o que eles fazem com o jornal. Assinantes se cadastram e recebem cada nova edição automaticamente. Quando perdem o interesse, cancelam a assinatura e param de receber edições — tudo isso sem o editor mudar seu processo de impressão.

Casos de uso

  • Tratamento de eventos de GUI — botões, inputs, e outros widgets notificam listeners sobre ações do usuário
  • Sincronização Model-View em arquiteturas MVC/MVVM
  • Feeds de dados em tempo real, como cotações de ações, mensagens de chat, ou leituras de sensores
  • Sistemas de mensageria pub/sub e event buses
  • Streams reativos e sistemas de detecção de mudança (ex: RxJS, MobX, reatividade do Vue)

Exemplos de código

UserService.java
import java.util.HashSet;
import java.util.Set;

interface Observer<T> {
    void update(String event, T data);
}

record UserData(int id, String email) {}

final class EventEmitter<T> {
    private final Set<Observer<T>> observers = new HashSet<>();

    void subscribe(Observer<T> observer) {
        observers.add(observer);
    }

    void unsubscribe(Observer<T> observer) {
        observers.remove(observer);
    }

    void emit(String event, T data) {
        for (Observer<T> observer : observers) {
            observer.update(event, data);
        }
    }
}

final class Logger implements Observer<UserData> {
    @Override
    public void update(String event, UserData data) {
        System.out.println("[log] " + event + ": user #" + data.id());
    }
}

final class EmailNotifier implements Observer<UserData> {
    @Override
    public void update(String event, UserData data) {
        if (event.equals("user_registered")) {
            System.out.println("Sending welcome email to " + data.email());
        }
    }
}

final class UserService {
    private final EventEmitter<UserData> events = new EventEmitter<>();

    EventEmitter<UserData> events() {
        return events;
    }

    void register(int id, String email) {
        UserData user = new UserData(id, email);
        events.emit("user_registered", user);
    }
}

public class ObserverDemo {
    public static void main(String[] args) {
        UserService service = new UserService();
        service.events().subscribe(new Logger());
        service.events().subscribe(new EmailNotifier());

        service.register(1, "ana@example.com");
    }
}

Um emissor de eventos do UserService que notifica observers Logger e EmailNotifier quando um evento user_registered dispara, demonstrando assinaturas tipadas e gerenciamento de observers.