Padrões de Projeto Observer – Modelagem e Prática

Design Patterns Observer

Neste artigo abordaremos a modelagem e prática de um padrão de projeto (Design Patterns) bastante importante do GoF (Gang of Four) que é o Padrão Observer.

A essência desse padrão está na possibilidade de uma classe poder fazer uma 'notificação' a um conjunto de classes associadas de que o Estado (Conjunto de Atributos) dela foi alterado por algum motivo. A definição do padrão observer descrita pelos criadores é a seguinte:

"Definir uma dependência um-para-muitos entre objetos para que quando um objeto mudar de estado, todos os seus dependentes sejam notificados e atualizados automaticamente." [GoF]

Você deve estar pensando "Eu posso fazer essa notificação manualmente ou até mesmo utilizando eventos como actionPerformed para fazer a notificação através de chamas a métodos de cada instância (ufaa)". Porém dessa maneira você estará criado um sistema com alto acoplamento e de difícil manutenabilidade.

Utilizando o padrão Observer conseguimos reduzir o uso do relacionamento bidirecional por meio de interfaces e classes abstratas, separando a abstração para ter um alto nível de coesão e baixo acoplamento.

Para ficar mais intuitivo vamos fazer o uso de exemplos. Imagine um operário de obras que esta trabalhando em uma construção junto com seus 'companheiros'.. O fato de uma sirene tocar implica em uma mudança (mudança de estado), que pode ser um sinal para a hora do almoço, pode ser o final do expediente e etc.

Temos então o seguinte ambiente, o objeto observável (Sirene) e os observadores (Operários). Quando o objeto observável alterar o seu estado, enviará um sinal sonoro (mensagem) alertando os objetos observadores.

Padrões de Projeto - Observer









Vamos definir as abstrações, chamaremos de Operário a interface do operário e Sirene a interface da sirene como mostrado abaixo:

Padrões de Projeto - Observer Interface

Perceba que as duas abstrações se relacionam entre si, relacionamento entre interfaces, ou seja, estamos deixando nossa aplicação mais independente separando-as das classes concretas

Na interface Sirene nós especificamos parte do que ela poderá fazer, no caso adicionar ou retirar um observador, da mesma maneira seguiremos com o Operário, sendo que, no mais alto nível de abstração, o minimo que um observador irá sofrer é o fato de ser notificado (atualizar). Se o operário vai ou não responder ao chamado não cabe a interface.

Com as interfaces em mãos podemos montar nosso diagrama, criaremos duas classes que iram implementar essas interfaces, sendo elas SireneConcreta e OperarioConcreto, conforme vemos abaixo:

Diagrama de Classe Observer

Na classe SireneConcreta criaremos todos os outros métodos necessários para o bom funcionamento da sirene e o método notificarObservadores que ira "varrer" o ArrayList que contem uma lista de observadores.

A classe concreta não foge a regra, irá implementar além do método atualizar todos os métodos dos operários.

Tendo toda a estrutura diagramatizada você poderá utilizar o próprio programa de modelagem (se estiver disponível) para gerar o código da nossa estrutura. Eu estou utilizando para esse fim o Enterprise Architect, porém qualquer um com suporte a UML 2.0 (de preferência) poderá ser usado, seja Netbeans, ArgoUML, ou plugins para eclipse..

Implementando nossas interfaces, teremos,
Interface Sirene:

 
public interface Sirene {
 
	public void adicionarObservador( Operario o );
	public void removerObservador( Operario o );
 
}
 

Interface Operario:

 
public interface Operario {
 
	public void atualizar(Sirene s);
 
}
 

Com as nossas abstrações prontas, podemos dar inicio a construção das classes concretas, teremos,
Classe SireneConcreta:

 
import java.util.ArrayList;
import java.util.Iterator;
 
public class SireneConcreta implements Sirene {
 
	private Boolean alertaSonoro = false;
	private ArrayList observadores = new ArrayList();
 
	public void alterarAlerta(){
		if(alertaSonoro)
			alertaSonoro = false;
		else
			alertaSonoro = true;
		notificarObservadores();
	}
 
	public Boolean getAlerta(){
		return alertaSonoro;
	}
 
	public void adicionarObservador(Operario o) {
		observadores.add(o);
	}
 
	public void removerObservador(Operario o) {
		observadores.remove(o);
	}
 
	private void notificarObservadores(){
		Iterator i = observadores.iterator();
		while(i.hasNext()){
			Operario o = (Operario) i.next();
			o.update(this);
		}
	}
}
 

Perceba que chamamos o método notificarObservadores exatamente quando o objeto SireneConcreta altera seu estado. Na implementação do método notificarObservadores nos fazemos uma varredura simples e informando a cada instância dele mesmo que o objeto que estava sendo observado mudou seu estado. Repare que estamos trabalhando sempre com as Interfaces ao invés das classes concretas.

Classe OperarioConcreto:

 
public class OperarioConcreto implements Operario {
 
	private SireneConcreta objetoObservado;
 
	public OperarioConcreto(SireneConcreta o){
		this.objetoObservado = o;
		objetoObservado.adicionarObservador(this);
	}
 
	public void atualizar(Sirene s) {
		if(s == objetoObservado){
			System.out.println("[INFO] A Sirene mudou seu
estado para: " + objetoObservado.getAlerta());
		}
	}
}
 

Com o construtor dessa maneira podemos ao passo de criar o observador e já setar como parâmetro o objeto observado, logo abaixo podemos chamar o método adicionarObservador que passa por referência a sua própria instância!. Interessante, não?

Quando o método atualizar é chamado nós precisamos checar se a sirene que alterou o estado é a mesma que estamos observando.

Com toda estrutura implementada é hora de testar, para tal vamos criar a classe GerenciadorSirene:

 
public class GerenciadorSirene {
 
	public static void main(String[] args) {
 
		SireneConcreta sirene = new SireneConcreta();
		// Sirete ja começa com valor default false
 
		OperarioConcreto obs1 = new OperarioConcreto(sirene);
		OperarioConcreto obs2 = new OperarioConcreto(sirene);
		// Já passando a sirene como parametro
 
		sirene.alterarAlerta();
		// Nesse momento é chamado o método atualizar
		// das instâncias obs1 e obs2, saída:
		// [INFO] A Sirene mudou seu estado para: true
		// [INFO] A Sirene mudou seu estado para: true
 
		sirene.alterarAlerta();
		//[INFO] A Sirene mudou seu estado para: false
		//[INFO] A Sirene mudou seu estado para: false
 
		// Obs: 2 saídas porque temos 2 observadores
	}
}
 

É possível dar nomes aos observadores criando um atributo String dentro de OperatorConcreto e passando seu nome para o construtor como segundo parâmetro, para ficar mais fácil diferenciá-los.

Observer Codigo-Fonte


Dessa maneira, termino esse artigo sobre o Padrão de Projeto Observer em caso de dúvidas, entre em contato! Bom proveito!




 
 
 

7 Respostas para “Padrões de Projeto Observer – Modelagem e Prática”

  1. Sidney 4.06.2009 as 13:16
    Internet Explorer 8.0Windows XP


    Yeah !!! muito bom!

  2. Ricardo Henry 5.06.2009 as 23:41
    Firefox 3.0.10Windows Vista


    Mesmo eu não entendendo muita coisa do que li, achei muito bom!! Parabéns aee Rafa!!

  3. Pedro 23.06.2009 as 18:22
    Firefox 3.0.11Windows XP


    muito bom, ta manjando hein rafa…

  4. rael 4.07.2009 as 19:02
    Firefox 3.5GNU/Linux


    Essa foi a melhor explicação sobre esse padrão que ja achei, finalmente consegui entender esse padrão, parabéns

  5. Mastter 9.10.2009 as 13:01
    Firefox 3.0.14Windows Vista


    Paraabéns, explicação que me ajudou muito com um trabalho de faculdade.Consegui entender muito bem.

  6. Vídeo Aula – Padrões de Projeto Strategy UML JAVA e PHP | Rafael Capucho 3.11.2009 as 19:03
    WordPress 2.8.5


    [...] você se interessa por Padrão de Projeto veja o artigo Padrão de Projeto Observer [...]

  7. Renato 9.03.2010 as 12:45
    Firefox 3.5.8Windows XP


    O Padrão de Projeto Observer é ótimo…
    Uma Excelente explicação Raquel…
    O pessoal só tem q sacar quando o usar o padrão q é o mais complicado!!!