Padrões de Projeto Observer – Modelagem e Prática
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.

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

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:

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.
Dessa maneira, termino esse artigo sobre o Padrão de Projeto Observer em caso de dúvidas, entre em contato! Bom proveito!










Yeah !!! muito bom!
Mesmo eu não entendendo muita coisa do que li, achei muito bom!! Parabéns aee Rafa!!
muito bom, ta manjando hein rafa…
Essa foi a melhor explicação sobre esse padrão que ja achei, finalmente consegui entender esse padrão, parabéns
Paraabéns, explicação que me ajudou muito com um trabalho de faculdade.Consegui entender muito bem.
[...] você se interessa por Padrão de Projeto veja o artigo Padrão de Projeto Observer [...]
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!!!