Padrão de Projeto Observer
Definição
O padrão Observer, também conhecido como padrão Publish-Subscribe, define uma relação de dependência um-para-muitos entre objetos.
Um objeto sujeito pode ser monitorado por múltiplos objetos observadores simultaneamente, garantindo que sempre que o estado do objeto sujeito mudar, todos os objetos dependentes sejam notificados e atualizados automaticamente.
Este é um padrão comportamental.
Cenários de Aplicação
- Quando um modelo abstrato contém dois aspectos de conteúdo, onde um aspecto depende do outro;
- Quando um ou mais objetos precisam mudar em resposta às alterações em outro objeto;
- Para implementar funcionalidades semelhantes a mecanismos de transmissão, onde não é necessário conhecer os receptores específicos, apenas difundir;
- Para criar encadeamentos multinível, formando um mecanismo de gatilho em cadeia que permite notfiicações cruzadas de eventos.
Exemplos de Código
Implementação Manual do Padrão Observer
Vamos utilizar o exemplo de um mouse:
1. Criação da classe de evento:
package com.exemplo.padroes.observer.mouse;
import java.lang.reflect.Method;
public class MouseEvent {
private String descricao;
private Object origem;
private Object destino;
private Method metodoAlvo;
private String timestamp;
public MouseEvent(String descricao) {
this.descricao = descricao;
}
public MouseEvent(Object destino, Method metodoAlvo) {
this.destino = destino;
this.metodoAlvo = metodoAlvo;
}
public String getDescricao() {
return descricao;
}
public Object getOrigem() {
return origem;
}
public Object getDestino() {
return destino;
}
public Method getMetodoAlvo() {
return metodoAlvo;
}
public String getTimestamp() {
return timestamp;
}
public MouseEvent setDescricao(String descricao) {
this.descricao = descricao;
return this;
}
public MouseEvent setOrigem(Object origem) {
this.origem = origem;
return this;
}
public MouseEvent setDestino(Object destino) {
this.destino = destino;
return this;
}
public MouseEvent setMetodoAlvo(Method metodoAlvo) {
this.metodoAlvo = metodoAlvo;
return this;
}
public MouseEvent setTimestamp(String timestamp) {
this.timestamp = timestamp;
return this;
}
}
2. Criação da classe de escuta de eventos, que chama os métodos correspondentes no alvo quando um evento é disparado:
package com.exemplo.padroes.observer.mouse;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class MouseObservador {
protected Map<string mouseevent=""> eventos = new HashMap<>();
public void adicionarEvento(String tipoEvento, Object alvo, Method metodoAlvo) {
eventos.put(tipoEvento, new MouseEvent(alvo, metodoAlvo));
}
public void adicionarEvento(String tipoEvento, Object alvo) throws NoSuchMethodException {
Method metodoAlvo = alvo.getClass().getMethod("ao" + converterPrimeiraLetraMaiuscula(tipoEvento), MouseEvent.class);
adicionarEvento(tipoEvento, alvo, metodoAlvo);
}
private String converterPrimeiraLetraMaiuscula(String tipoEvento) {
char[] caracteres = tipoEvento.toCharArray();
caracteres[0] = Character.toUpperCase(caracteres[0]);
return new String(caracteres);
}
protected void acionar(String tipoAcionamento) throws IllegalAccessException, InvocationTargetException {
if (!eventos.containsKey(tipoAcionamento)) {
return;
}
MouseEvent evento = eventos.get(tipoAcionamento);
evento.setOrigem(this);
evento.setTimestamp(String.valueOf(System.currentTimeMillis()));
if (evento.getMetodoAlvo() != null) {
evento.getMetodoAlvo().invoke(evento.getDestino(), evento);
}
}
}
</string>
3. Definição dos tipos de eventos do mouse:
package com.exemplo.padroes.observer.mouse;
public class TiposMouse {
public static final String AO_CLICAR = "clicar";
public static final String AO_DUPLO_CLIQUE = "duploClique";
public static final String AO_MOVER = "mover";
public static final String AO_ROLAR = "rolar";
}
4. Criação da classe do mouse:
package com.exemplo.padroes.observer.mouse;
import java.lang.reflect.InvocationTargetException;
public class DispositivoMouse {
private ObservadorMouse observador;
public void configurarObservador(ObservadorMouse observador) {
this.observador = observador;
}
public void clicar() {
System.out.println("Clique no mouse");
acionarEvento(TiposMouse.AO_CLICAR);
}
public void duploClique() {
System.out.println("Duplo clique no mouse");
acionarEvento(TiposMouse.AO_DUPLO_CLIQUE);
}
public void mover() {
System.out.println("Movimento do mouse");
acionarEvento(TiposMouse.AO_MOVER);
}
public void rolar() {
System.out.println("Rolar mouse");
acionarEvento(TiposMouse.AO_ROLAR);
}
private void acionarEvento(String tipoEvento) {
if (observador != null) {
try {
observador.disparar(tipoEvento);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
}
5. Criação da classe de retorno:
package com.exemplo.padroes.observer.mouse;
public class RespostaMouse {
public void aoClicar(MouseEvent evento) {
System.out.println("Resposta ao clique");
}
public void aoDuploClicar(MouseEvent evento) {
System.out.println("Resposta ao duplo clique");
}
}
6. Teste:
package com.exemplo.padroes.observer.mouse;
public class TesteMouse {
public static void main(String[] args) throws NoSuchMethodException {
DispositivoMouse mouse = new DispositivoMouse();
RespostaMouse resposta = new RespostaMouse();
ObservadorMouse observador = new ObservadorMouse();
observador.adicionarEvento(TiposMouse.AO_CLICAR, resposta);
mouse.configurarObservador(observador);
System.out.println("====== Clique no mouse ======");
mouse.clicar();
System.out.println("====== Duplo clique no mouse ======");
mouse.duploClique();
}
}
Padrão Observer Fornecido pelo JDK
1. Criação de uma classe que implementa a interface Observer:
package com.exemplo.padroes.observer.jdk;
import java.util.Observable;
import java.util.Observer;
public class ObservadorCustomizado implements Observer {
private String nome;
public ObservadorCustomizado(String nome) {
this.nome = nome;
}
@Override
public void update(Observable observable, Object argumento) {
AlvoObservable alvo = (AlvoObservable)observable;
System.out.println(nome + " recebeu mensagem: " + argumento);
}
}
2. Criação de uma subclasse de Observable:
package com.exemplo.padroes.observer.jdk;
import java.util.Observable;
public class AlvoObservable extends Observable {
@Override
public synchronized void addObserver(java.util.Observer observador) {
super.addObserver(observador);
}
public void enviarMensagem(String mensagem) {
System.out.println("Publicando mensagem: " + mensagem);
setChanged();
notifyObservers(mensagem);
}
}
3. Classe de teste:
package com.exemplo.padroes.observer.jdk;
public class TesteObserver {
public static void main(String[] args) {
AlvoObservable alvo = new AlvoObservable();
ObservadorCustomizado obs1 = new ObservadorCustomizado("usuario1");
ObservadorCustomizado obs2 = new ObservadorCustomizado("usuario2");
alvo.addObserver(obs1);
alvo.addObserver(obs2);
System.out.println("======= Primeira notificação =======");
alvo.enviarMensagem("Reunião amanhã às 14h!");
System.out.println("======= Segunda notificação =======");
alvo.enviarMensagem("Treinamento na próxima semana!");
}
}
Padrão Observer Fornecido pelo Guava
1. Adição da dependência do Guava:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
2. Criação da classe de evento:
package com.exemplo.padroes.observer.guava;
import com.google.common.eventbus.Subscribe;
public class EventoGuava {
@Subscribe
public void receberNotificacao(String mensagem) {
System.out.println("Método receberNotificacao chamado com parâmetro: " + mensagem);
}
}
3. Classe de teste:
package com.exemplo.padroes.observer.guava;
import com.google.common.eventbus.EventBus;
public class TesteEventoGuava {
public static void main(String[] args) {
EventBus barramento = new EventBus();
EventoGuava evento = new EventoGuava();
barramento.register(evento);
barramento.post("mensagem de teste");
}
}