CodeGym /Cursos /JAVA 25 SELF /Padrão Observer (observador)

Padrão Observer (observador)

JAVA 25 SELF
Nível 50, Lição 3
Disponível

1. Conhecendo o padrão “Observer”

O padrão “Observer” — um dos mais conhecidos e fundamentais padrões de projeto. Ele descreve uma situação em que um objeto (observável, ou subject) informa sobre suas mudanças a outros objetos (observadores), que se inscreveram para receber essas mudanças.

Falando de forma simples: temos um canal do Telegram (objeto observável) e “assinantes” (observadores). Sempre que sai um novo post, o canal notifica todos os assinantes, e eles decidem o que fazer — ler, ignorar ou cancelar a inscrição.

Na programação, esse padrão permite notificar automaticamente objetos interessados sobre eventos ou mudanças de estado, sem acoplá-los diretamente entre si. Isso é importante para construir sistemas flexíveis, extensíveis e fáceis de manter.

Onde o padrão “Observer” aparece?

  • Em interfaces gráficas (Swing, AWT, JavaFX) — ouvintes de eventos.
  • Em bibliotecas reativas (RxJava, Project Reactor).
  • Na lógica de negócios: reagir à mudança de estado do modelo.
  • Em engines de jogos (eventos de colisão, vitória, derrota etc.).
  • Em qualquer lugar onde seja preciso separar “o que aconteceu” de “o que fazer com isso”.

Relação do padrão com eventos e ouvintes no Java

De fato, todo o modelo de eventos do Java é construído sobre o “Observer”. Quando você escreve button.addActionListener(listener);, você está implementando esse padrão:

  • Observável — o botão (ou outro componente).
  • Observador — seu ouvinte, que implementa o método actionPerformed().
  • Evento — o usuário clicou, passou o mouse etc.
  • Notificação — o componente chama actionPerformed().

Tudo isso é uma implementação clássica de Observer!

2. Implementação clássica do padrão “Observer”

Vamos ver como implementar o padrão em suas próprias classes — sem Swing e AWT, para perceber que não há mágica.

Elementos principais do padrão

  • Observable (Subject) — objeto observável. Armazena a lista de observadores e os notifica sobre mudanças.
  • Observer — interface do observador, geralmente com o método update().

Exemplo: Termômetro e ar‑condicionado

Interface do observador

public interface TemperatureObserver {
    void temperatureChanged(int newTemperature);
}

Classe “Termômetro” (observável)

import java.util.*;

public class Thermometer {
    private int temperature;
    private final List<TemperatureObserver> observers = new ArrayList<>();

    public void addObserver(TemperatureObserver observer) {
        observers.add(observer);
    }

    public void removeObserver(TemperatureObserver observer) {
        observers.remove(observer);
    }

    public void setTemperature(int newTemperature) {
        if (this.temperature != newTemperature) {
            this.temperature = newTemperature;
            notifyObservers();
        }
    }

    private void notifyObservers() {
        for (TemperatureObserver observer : observers) {
            observer.temperatureChanged(temperature);
        }
    }
}

Exemplo de observador — “Ar‑condicionado”

public class AirConditioner implements TemperatureObserver {
    @Override
    public void temperatureChanged(int newTemperature) {
        if (newTemperature > 25) {
            System.out.println("Ar-condicionado ligado! Está quente: " + newTemperature + "°C");
        } else {
            System.out.println("Ar-condicionado desligado. Temperatura: " + newTemperature + "°C");
        }
    }
}

Uso

public class Main {
    public static void main(String[] args) {
        Thermometer thermometer = new Thermometer();
        AirConditioner conditioner = new AirConditioner();

        thermometer.addObserver(conditioner);

        thermometer.setTemperature(22); // Ar-condicionado desligado. Temperatura: 22°C
        thermometer.setTemperature(28); // Ar-condicionado ligado! Está quente: 28°C
    }
}

Eis toda a mágica! Você pode adicionar mais cem observadores — todos receberão notificações quando a temperatura mudar.

Diagrama do padrão

flowchart LR
    T["Termômetro (Observable)"] -- notifica --> AC["Ar-condicionado (Observer)"]
    T -- notifica --> L["Logger (Observer)"]
    T -- notifica --> Alarm["Alarme (Observer)"]

Detalhes modernos: Observable obsoleto e novas abordagens

Na biblioteca padrão do Java existiam java.util.Observable e java.util.Observer, mas desde o Java 9 foram marcados como obsoletos (deprecated). O motivo — falta de flexibilidade (por exemplo, Observable é uma classe, não uma interface, o que dificulta herdar de outra classe).

A abordagem moderna é projetar suas próprias interfaces de ouvintes e a lógica de inscrição/cancelamento (como no exemplo acima). Isso é mais flexível, seguro e se ajusta melhor às tarefas reais.

3. Exemplo: miniaplicativo com assinantes

Vamos fazer um “contador de cliques” com a possibilidade de assinar a mudança do valor.

Interface do ouvinte

public interface CounterListener {
    void counterChanged(int newValue);
}

Classe contador

import java.util.*;

public class Counter {
    private int value = 0;
    private final List<CounterListener> listeners = new ArrayList<>();

    public void addCounterListener(CounterListener l) {
        listeners.add(l);
    }

    public void removeCounterListener(CounterListener l) {
        listeners.remove(l);
    }

    public void increment() {
        value++;
        notifyListeners();
    }

    private void notifyListeners() {
        for (CounterListener l : listeners) {
            l.counterChanged(value);
        }
    }

    public int getValue() {
        return value;
    }
}

Ouvinte: imprimir mensagem

public class ConsoleCounterListener implements CounterListener {
    @Override
    public void counterChanged(int newValue) {
        System.out.println("O contador mudou: " + newValue);
    }
}

Uso

public class Main {
    public static void main(String[] args) {
        Counter counter = new Counter();
        counter.addCounterListener(new ConsoleCounterListener());

        counter.increment(); // O contador mudou: 1
        counter.increment(); // O contador mudou: 2
    }
}

4. Detalhes úteis

Alternativas e extensões modernas

Em projetos reais, muitas vezes se usam classes anônimas ou expressões lambda para se inscrever: counter.addCounterListener(newValue -> System.out.println("Novo valor: " + newValue));

(Para fazer isso, a interface deve ser funcional — ter um único método abstrato.)

Também são populares as bibliotecas reativas (RxJava, Project Reactor), onde o “Observer” é implementado com suporte a fluxos de eventos, filtragem, assincronia etc. Para entender a essência, basta o esquema clássico analisado acima.

Aplicações do padrão “Observer” no dia a dia

  • Modelos de dados. Mudanças no modelo (lista de tarefas, produtos, usuários) notificam as visualizações para atualização.
  • Registro (logging). Um assinante de log reage a eventos em todo o sistema.
  • Notificações. Ao mudar o estado — envio de email, notificações push, mensagens no Telegram.
  • Jogos. Mudança de saúde, aparecimento de inimigos, conclusão de nível.
  • Várias threads. Uma thread publica eventos, outras reagem.

5. Erros comuns ao implementar o padrão “Observer”

Erro nº 1: Esqueceu de remover o ouvinte. Se o ouvinte não é mais necessário, mas não foi removido, ele continuará recebendo notificações. Em aplicações de longa duração isso pode levar a vazamentos de memória.

Erro nº 2: Operações longas ou bloqueantes nos handlers. Se o handler executa trabalho pesado (E/S, BD), o aplicativo pode “travar”, especialmente se as notificações vêm da thread de UI. Transfira tarefas pesadas para threads em segundo plano.

Erro nº 3: Exceções nos ouvintes. Uma exceção em um ouvinte pode interromper o envio aos demais. Encapsule as chamadas dos ouvintes em try-catch e registre os erros em log.

Erro nº 4: Registro múltiplo do mesmo ouvinte. Se um ouvinte for adicionado várias vezes, ele receberá o evento o mesmo número de vezes. Controle o registro e aplique proteção contra adição duplicada.

Erro nº 5: Forte acoplamento entre observável e observador. Se o observável conhece implementações concretas dos observadores, a fraca dependência é rompida. Use apenas interfaces (por exemplo, TemperatureObserver, CounterListener).

1
Tarefa
JAVA 25 SELF, nível 50, lição 3
Bloqueado
Fundando a Agência de Notícias Tech 📰
Fundando a Agência de Notícias Tech 📰
1
Tarefa
JAVA 25 SELF, nível 50, lição 3
Bloqueado
Distribuição de notícias quentes 🌐
Distribuição de notícias quentes 🌐
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION