Padrão de Projeto Observer - Implementação e Exemplos

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

  1. Quando um modelo abstrato contém dois aspectos de conteúdo, onde um aspecto depende do outro;
  2. Quando um ou mais objetos precisam mudar em resposta às alterações em outro objeto;
  3. Para implementar funcionalidades semelhantes a mecanismos de transmissão, onde não é necessário conhecer os receptores específicos, apenas difundir;
  4. 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");
	}
}

Tags: java observer-pattern publish-subscribe event-driven design-patterns

Publicado em 9-23 00:31