O Padrão do Observador em Java

O Padrão do Observador em Java

1. Visão geral

Neste artigo, vamos descrever o padrão Observer e dar uma olhada em algumas alternativas de implementação Java.

2. O que é o padrão Observer?

Observador é um padrão de design comportamental. Ele especifica a comunicação entre objetos:observable eobservers. An observable is an object which notifies observers about the changes in its state.

Por exemplo, uma agência de notícias pode notificar canais quando recebe notícias. Receber notícias é o que muda o estado da agência de notícias e faz com que os canais sejam notificados.

Vamos ver como podemos implementá-lo nós mesmos.

Primeiro, vamos definir a classeNewsAgency:

public class NewsAgency {
    private String news;
    private List channels = new ArrayList<>();

    public void addObserver(Channel channel) {
        this.channels.add(channel);
    }

    public void removeObserver(Channel channel) {
        this.channels.remove(channel);
    }

    public void setNews(String news) {
        this.news = news;
        for (Channel channel : this.channels) {
            channel.update(this.news);
        }
    }
}

NewsAgency é um observável e quandonews é atualizado, o estado deNewsAgency muda. Quando a mudança acontece,NewsAgency notifica os observadores sobre esse fato chamando seu métodoupdate().

To be able to do that, the observable object needs to keep references to the observers, e no nosso caso, é a variávelchannels.

Vamos agora ver como o observador, a classeChannel pode ser. Deve ter o métodoupdate() que é invocado quando o estado deNewsAgency muda:

public class NewsChannel implements Channel {
    private String news;

    @Override
    public void update(Object news) {
        this.setNews((String) news);
    }
}

A interfaceChannel tem apenas um método:

public interface Channel {
    public void update(Object o);
}

Agora, se adicionarmos uma instância deNewsChannel à lista de observadores,e alterar o estado deNewsAgency, a instância deNewsChannel será atualizada:

NewsAgency observable = new NewsAgency();
NewsChannel observer = new NewsChannel();

observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");

Há uma interfaceObserver predefinida nas bibliotecas principais do Java, o que torna a implementação do padrão do observador ainda mais simples. Vamos dar uma olhada nisso.

3. Implementação comObserver

A interfacejava.util.Observer define o métodoupdate(), portanto, não há necessidade de defini-lo como fizemos na seção anterior.

Vamos ver como podemos usá-lo em nossa implementação:

public class ONewsChannel implements Observer {

    private String news;

    @Override
    public void update(Observable o, Object news) {
        this.setNews((String) news);
    }
}

Aqui, o segundo argumento vem deObservable, como veremos abaixo.

Para definir o, observável, precisamos estender a classeObservable de Java:

public class ONewsAgency extends Observable {
    private String news;

    public void setNews(String news) {
        this.news = news;
        setChanged();
        notifyObservers(news);
    }
}

Observe que não precisamos chamar o métodoupdate() do observador diretamente. Apenas chamamosstateChanged()enotifyObservers(), e a classeObservable está fazendo o resto para nós.

Além disso, contém uma lista de observadores e expõe métodos para manter essa lista -addObserver()edeleteObserver().

Para testar o resultado, basta adicionar o observador a esta lista e definir as notícias:

ONewsAgency observable = new ONewsAgency();
ONewsChannel observer = new ONewsChannel();

observable.addObserver(observer);
observable.setNews("news");
assertEquals(observer.getNews(), "news");

A interfaceObserver não é perfeita e está obsoleta desde o Java 9. Um dos seus contras é queObservable não é uma interface, mas uma classe, é por isso que as subclasses não podem ser usadas como observáveis.

Além disso, um desenvolvedor pode substituir alguns dos métodos sincronizados deObservable e interromper sua segurança de thread.

Vejamos a interfaceProperyChangeListener, que é recomendada em vez de usarObserver.

4. Implementação comPropertyChangeListener

In this implementation, an observable must keep a reference to the PropertyChangeSupport instance. Ajuda a enviar notificações aos observadores quando uma propriedade da classe é alterada.

Vamos definir o observável:

public class PCLNewsAgency {
    private String news;

    private PropertyChangeSupport support;

    public PCLNewsAgency() {
        support = new PropertyChangeSupport(this);
    }

    public void addPropertyChangeListener(PropertyChangeListener pcl) {
        support.addPropertyChangeListener(pcl);
    }

    public void removePropertyChangeListener(PropertyChangeListener pcl) {
        support.removePropertyChangeListener(pcl);
    }

    public void setNews(String value) {
        support.firePropertyChange("news", this.news, value);
        this.news = value;
    }
}

Usando estesupport, podemos adicionar e remover observadores e notificá-los quando o estado do observável muda:

support.firePropertyChange("news", this.news, value);

Aqui, o primeiro argumento é o nome da propriedade observada. O segundo e o terceiro argumentos são seu antigo e novo valor de acordo.

Os observadores devem implementarPropertyChangeListener:

public class PCLNewsChannel implements PropertyChangeListener {

    private String news;

    public void propertyChange(PropertyChangeEvent evt) {
        this.setNews((String) evt.getNewValue());
    }
}

Devido à classePropertyChangeSupport que está fazendo a fiação para nós, podemos restaurar o novo valor da propriedade do evento.

Vamos testar a implementação para ter certeza de que ela também funciona:

PCLNewsAgency observable = new PCLNewsAgency();
PCLNewsChannel observer = new PCLNewsChannel();

observable.addPropertyChangeListener(observer);
observable.setNews("news");

assertEquals(observer.getNews(), "news");

5. Conclusão

Neste artigo, examinamos duas maneiras de implementar o padrão de designObserver em Java, com a abordagemPropertyChangeListener sendo a preferida.

O código-fonte do artigo está disponívelover on GitHub.