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 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:
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:
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:
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.
Artigo original de: http://blog.rafaelcapucho.com/
Ferrado hein cara? Observer é um pattern fudido!
ResponderExcluirNão entendi muito bem ele funciona como plugin?
ResponderExcluir