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 mínimo 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 vão 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!


 
 
 

17 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!!!

  8. Neto 12.03.2010 as 16:03
    Firefox 3.5.8Windows 7


    ola rafael, muito bom seu artigo, vc conhece alguam exemplo pratico do mundo real, que utiliza os padroes.. estou tendo dificuldades para encontrar praticidade em coisas reais, geralmente os livros trazem exemplos mais academicos. se souber me avisa.. to querendo aplica-los. obg.

  9. Rafael Capucho 12.03.2010 as 21:48
    Firefox 3.5.8Ubuntu 9.10


    Olá Neto,

    Primeiramente obrigado pelo apoio,
    Veja, o Padrão Observer é bastante utilizado em bibliotecas de funçṍes e principalmente em quase todos as bibliotecas de GUI (interface gráfica).. assim ele consegue monitorar o evento de cliques ou algo do gênero.

    conheça exemplo de aplicações clicando aqui: http://en.wikipedia.org/wiki/Observer_pattern#Implementations

    Forte Abraço,
    Rafael Capucho.

  10. Tiago Carmo Santos 17.03.2010 as 16:46
    Internet Explorer 8.0Windows 7


    Muito cara, o padrão foi muito bem explicado.
    De facil entendimento XD

  11. Daniel 24.05.2010 as 19:01
    Internet Explorer 8.0Windows 7


    Parabens pelo artigo! Muito útil e muito bem explicado!

  12. Vicente Hercílio 31.05.2010 as 18:16
    Google Chrome 5.0.375.55Windows Vista


    Muito didático este artigo, exemplos claros e bom texto. Estava a ler um livro bem cansativo sobre o tema e ao encontrar este artigo e ao ler conseguir entender as coisas bem melhor.
    Parabéns e obrigado pela postagem.

  13. Diana Maia 10.06.2010 as 18:13
    Firefox 3.6.3Windows 7


    Olá, Rafael, você indicar onde posso encontrar um exemplo de como usar o padrão Observer com RMI??

  14. Rafael Capucho 10.06.2010 as 18:40
    Firefox 3.5.2GNU/Linux


    Diana Maia,

    Olá Diana, veja eu não imagino qual seria o problema na integração entre Observer e RMI ou até mesmo o Corba, a questão é que eles não fazem integração entre si, mas sim uma chamada lançada por um evento.

    Se um evento ocorrer no Objeto Observado, você a partir do evento, fazer uma requisição RMI, normalmente. Não consigo entender onde é a dificuldade.

    Com atenção,
    Rafael Capucho.

  15. Erik 19.04.2011 as 01:05
    Internet Explorer 8.0Windows 7 x64 Edition


    Realmente muito bom essa explicação sobre o padrão Observer. Andei ralando muito pra entender o mecanismo disso, mas finalmente entendi!

    Muitos sites mostram exemplos complexos demais. Esse aí do alarme foi bem simples e claro.

    Obrigado e parabéns!

  16. Diego Sousa 31.12.2011 as 02:52
    Google Chrome 16.0.912.63GNU/Linux x64


    Olá Rafael Parabéns,

    Apenas uma correção, creio que no método notificarObservadores() da Classe SireneConcreta, quando se dar o.update(this), talvez o certo seria o.atualizar(this). ou não?

    A correção é simples, mas sem ela, esse erro poderá gerar duvidas em muitos iniciante.

    Parabéns Novamente, muito Boa a explicação.

    []’s

  17. orlando 12.01.2012 as 13:37
    Firefox 9.0.1Windows Vista


    Muito Bom cara! Ótima explicação, e o exemplo que vc foi ótimo também!

    Muito obrigado. Finalmente entendi!