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.