Observer

Objetivo

Também chamado de

Exemplo

O problema

A solução idiomática em Java

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

Estrutura

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 telefoneListeners = new Vector();

    public void tocaFone() {
        disparaTelefoneTocou();
    }

    public void atendeFone() {
        disparaTelefoneAtendido();
    }

    public synchronized void addTelefoneListener(
                TelefoneListener l) {

        if (telefoneListeners.contains(l)) {
            return;
        }
        telefoneListeners.add(l);
    }

    public synchronized void 
            removeTelefoneListener(TelefoneListener l) {

        telefoneListeners.remove(l);
    }

    private void disparaTelefoneTocou() {
        Collection tl;
        synchronized (this) {
            // A interface Collection não tem clone()
            // mas a classe AbstractCollection tem.
            // Clonar para evitar problemas de sincronização
            // durante a propagação
            tl = ((AbstractCollection)telefoneListeners).clone();
        }
        Iterator it = tl.iterator();
        if(!it.hasNext()) {
            return;
        }
        TelefoneEvent evento = new TelefoneEvent(this);
        while(it.hasNext()) {
            ((TelefoneListener)(it.next())).telefoneTocou(evento);
        }
    }

    // disparaTelefoneAtendido() é semelhante a disparaTelefoneTocou()
    // Exercício: Que design pattern poderia ser usado para fatorar
    // o código comum?
    private void disparaTelefoneAtendido() {
        Collection tl;
        synchronized (this) {
            tl = ((AbstractCollection)telefoneListeners).clone();
        }
        Iterator it = tl.iterator();
        if(!it.hasNext()) {
            return;
        }
        TelefoneEvent evento = new TelefoneEvent(this);
        while(it.hasNext()) {
            ((TelefoneListener)(it.next())).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?

Consequê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

pat-10 programa anterior próxima