Observer

Problema

Objetivo

Também chamado de

Exemplo

O problema

A solução idiomática em Java

Estrutura

telefone1.gif (13745 bytes)

Etapa 1: Definir classes de categorias de eventos

Etapa 2: Define interfaces de Listener

    void telefoneTocou(TelefoneEvent e);

Etapa 3: Define classes de adaptação (opcional)

Etapa 4: Defina a classe observável Source

Etapa 5: Defina objetos Listener

Exemplo de código

public class TelefoneEvent
    extends java.util.EventObject {

    public TelefoneEvent(Telefone source) {
        super(source);
    }
}
public interface TelefoneListener
    extends java.util.EventListener {

    void telefoneTocou(TelefoneEvent e);
    void telefoneAtendido(TelefoneEvent e);
}
public class TelefoneAdapter
    implements TelefoneListener {

    void telefoneTocou(TelefoneEvent e) {}
    void telefoneAtendido(TelefoneEvent e) {}
}
import java.util.*;

public class Telefone {
    private Collection <TelefoneListener> telefoneListeners = new ArrayList<TelefoneListener>();

    // método de suporte para testar a solução
    public void tocaFone() {
        disparaTelefoneTocou();
    }

    // método de suporte para testar a solução
    public void atendeFone() {
        disparaTelefoneAtendido();
    }

    public synchronized void addTelefoneListener(TelefoneListener l) {
        if(!telefoneListeners.contains(l)) {
            telefoneListeners.add(l);
        }
    }

    public synchronized void 
            removeTelefoneListener(TelefoneListener l) {

        telefoneListeners.remove(l);
    }

    private void disparaTelefoneTocou() {
        Collection <TelefoneListener> tl;
        synchronized (this) {
            // Clonar para evitar problemas de sincronização
            // durante a propagação
            tl = (Collection)(((ArrayList)telefoneListeners).clone());
        }
        TelefoneEvent evento = new TelefoneEvent(this);                
        for (TelefoneListener t : tl) {
        	t.telefoneTocou(evento);
        }
    }
            

    // disparaTelefoneAtendido() é semelhante a disparaTelefoneTocou()
    // Exercício: Que design pattern poderia ser usado para fatorar
    // o código comum?
    private void disparaTelefoneAtendido() {
        Collection <TelefoneListener> tl;
        synchronized (this) {
            tl = (Collection)(((ArrayList)telefoneListeners).clone());
        }
        TelefoneEvent evento = new TelefoneEvent(this);
        for (TelefoneListener t : tl) {
        	t.telefoneAtendido(evento);
        }        
    }
}
public class SecretariaEletronica
    implements TelefoneListener {

    public void telefoneTocou(TelefoneEvent e) {
        System.out.println("Secretaria escuta o telefone tocando.");
    }

    public void telefoneAtendido(TelefoneEvent e) {
        System.out.println("Secretaria sabe que o telefone foi atendido.");
    }
}
public class Pessoa {
    public void escutaTelefone(Telefone t) {
        t.addTelefoneListener(
            new TelefoneAdapter() {
                public void telefoneTocou(TelefoneEvent e) {
                    System.out.println("Eu pego!");
                    ((Telefone)(e.getSource())).atendeFone();
                }
            }
        );
    }
}
public class ExemploFone {
    public static void main(String[] args) { 
        Telefone fone = new Telefone();
        Pessoa fulano = new Pessoa();
        SecretariaEletronica se = new SecretariaEletronica();

        fone.addTelefoneListener(se);
        fulano.escutaTelefone(fone);

        fone.tocaFone(); // começa a brincadeira
    }
}
Secretaria escuta o telefone tocando.
Eu pego!
Secretaria sabe que o telefone foi atendido.

Quando usar o padrão Observer?

Conseqüências do uso do padrão

Considerações de implementação

    super(novoValor); // Pai dispara o evento
    var = novoValor;  // atualiza estado do objeto (é tarde!)

Críticas sobre outras soluções do mesmo padrão

Perguntas finais para discussão

Ver também

programa