Padrão Observer em Java

Observer é um padrão de desenvolvimento de software que define uma relação de dependência de um para muitos (1-N) entre objectos, desta forma quando um objecto muda de estado todos os outros que se encontram dependentes deles são notificados e actualizados automaticamente.

É possível fazer uma analogia a um assinatura de um serviço,  por exemplo um serviço de noticias em que os assinantes são notificados sempre que há uma nova noticia. Eles podem ainda assinar ou cancelar a assinatura a qualquer momento.

Um exemplo de uso deste padrão pode ser visto no sistema de eventos da API Swing do Java, em que ao ocorrer determinado evento todos os objectos registados para esse tipo de evento são notificados de que ele ocorreu.

Embora existe na uma implementação deste padrão na API do Java, vamos antes fazer uma implementação manual que é semelhante para varias linguagens OO e em seguida passamos então à API.

O padrão é constituído por duas “peças” base, o Subject e os Observers.

  • Subject – Representa o serviço, controla o estado dos Observers.
  • Observer – Representa o assinante, estão dependentes do Subject para se actualizarem.

Na implementação do padrão devemos tentar ter acoplamento fraco entre os objectos que interagem, o Subject apenas conhece a interface Observer e não as suas implementações, os Observers podem ser adicionados e removidos em qualquer altura nem necessitar alterar o Subject e mudanças quer no Subject quer nos Observers não afectam um ao outro.

Para começar definimos o interface Subject

package observer;

/**
 * @author magician
 */
public interface Subject {

    /**
     * Adiciona um novo Observer à lista a notificar.
     */
    public void addObserver(Observer obs);

    /**
     * Remove um Observer da lista.
     */
    public void delObserver(Observer obs);

    /**
     * Notifica todos os Observers registados.
     */
    public void notifyObservers();

}

[Subject.java]

Em seguida definimos o interface Observer

package observer;

/**
 * @author magician
 */
public interface Observer {

    /**
     * Método que faz a notificação e recebe as actualizações.
     */
    public void update(Object obj);

}

[Observer.java]

Neste momento já temos os interfaces necessários para o padrão Observer, vamos agora passar à implementação.
Começamos por implementar a classe Reader que implementa o interface Observer

package observer;

/**
 * @author magician
 */
public class Reader implements Observer {

    /**
     * Ultima mensage lida
     */
    private String message;

    /**
     * Identificador do leitor, apenas para distinguir os prints
     */
    private int id;

    public Reader(int id) {
        this.id = id;
        this.message = "";
    }

    /**
     * Actualiza a ultima mensagem lida e imprime a mensagem
     */
    public void update(Object obj) {
        this.message = (String)obj;
        readAndPrint();
    }

    /**
     * Imprime a ultima mensagem lida
     */
    public void readAndPrint(){
        System.out.println(String.format("Reader %d -> Message: %s",
                this.id, this.message));
    }
}

[Reader.java]

Em seguida temos a implementação da classe MessageBoard que implementa o interface Subject

package observer;

import java.util.ArrayList;
import java.util.List;

/**
 * @author magician
 */
public class MessageBoard implements Subject {

    /**
     * Lista com todos os Observers a notificar.
     */
    private List <Observer> observers;

    /**
     * Ultima mensagem registada.
     */
    private String message;

    public MessageBoard(){
        this.observers = new ArrayList<Observer>();
    }

    public String getMessage() {
        return message;
    }

    /**
     * O método set para alem de atribui um novo valor à
     * mensagem executa o método para notificar os Observers.
     */
    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }

    /**
     * Adiciona um novo Observer a ser notificado
     */
    public void addObserver(Observer obs) {
        this.observers.add(obs);
    }

    /**
     * Remove um Observer da lista de Observers a serem notificados.
     */
    public void delObserver(Observer obs) {
        this.observers.remove(obs);
    }

    /**
     * Notifica e actualiza todos o observers registados.
     * Para alem de notificar envia também a nova mensagem.
     */
    public void notifyObservers() {
        for(Observer obs : this.observers){
            obs.update(this.message);
        }
    }
}

[MessageBoard.java]

Agora apenas falta criar a classe Main que ira criar um MessageBoard, alguns Readers e efectuar mudanças de estado no MessageBoard

package observer;

/**
 * @author magician
 */
public class Main {

    public static void main(String args []){
        MessageBoard board = new MessageBoard();
        Reader r1 = new Reader(1);
        Reader r2 = new Reader(2);
        Reader r3 = new Reader(3);

        /**
         * Adiciona os objectos a notificar
         */
        board.addObserver(r1);
        board.addObserver(r2);
        board.addObserver(r3);

        /**
         * Actualiza a ultima mensagem o que faz com que os Observers
         * sejam notificados da ocorrencia.
         */
        board.setMessage("Message 0001");
        board.setMessage("Message 0002");

        /**
         * Remove o Observer r2 da lista de Observers a registar.
         */
        board.delObserver(r2);

        /**
         * Actualiza novamente a utima mensagem.
         */
        board.setMessage("Message 0003");

        /**
         * Imprime a ultima mensagem lida pelo Observer r2
         */
        r2.readAndPrint();
    }
}

[Main.java]

Ao executar o Main obtemos o seguinte output

Reader 1 -> Message: Message 0001
Reader 2 -> Message: Message 0001
Reader 3 -> Message: Message 0001
Reader 1 -> Message: Message 0002
Reader 2 -> Message: Message 0002
Reader 3 -> Message: Message 0002
Reader 1 -> Message: Message 0003
Reader 3 -> Message: Message 0003
Reader 2 -> Message: Message 0002

Como se pode ver sempre que o nosso objecto MessageBoard é actualizado todos os Readers registados também o são automaticamente. Se reparar-mos as linhas 7, 8 e 9 do output diferem das sequências anteriores, isto acontece porque na linha 31 do Main o Reader r3 foi removido da lista de objectos a notificar e por isso na mensagem não foi actualizada.

A implementação é muito simples e incrivelmente útil como podemos ver, mas para quem não quer ou não acha necessário fazer esta implementação o Java já trás na sua API uma implementação deste padrão.

A classe java.util.Observable é uma implementação que corresponde ao nosso interface Subject do exemplo acima e o interface java.util.Observer é o correspondente ao nosso interface Observer do exemplo e neste caso a unica diferença está no método update que recebe a actualização e o objecto a actualizar face ao exemplo que apenas recebia a actualização.

Embora o uso da API poupe o trabalho da reimplementar o padrão ela tem uma desvantagem, o Subject neste caso o Observable é uma classe e não um interface e dado que em Java uma classe só pode ter uma super classe ao usar a classe Observable a classe que a irá implementar já não pode fazer extends a mais nenhuma classe.

Aqui podem encontrar um exemplo semelhante ao apresentado acima mas usando a API ao invés da implementação manual mostrada acima.

http://www.java2s.com/Code/Java/Design-Pattern/Observableandobserver.htm

Anúncios

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão /  Alterar )

Google photo

Está a comentar usando a sua conta Google Terminar Sessão /  Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão /  Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão /  Alterar )

Connecting to %s