Design Patterns

Danilo Sanches & Leonardo Rocha

Archive for the ‘Padrão Observer’ Category

Exemplos de Utilização do Padrão Observer

Trabalho da Disciplina Projeto e Tópicos Especiais em Projeto de Sistemas de Software

Posted by lsrocha em junho 6, 2009

 

Implementação de uma Pista de Boliche em JAVA utilizando o Pattern Observer

 

Autores

  • Danilo Sanches de Lima
  • Leonardo Santos Rocha

1.  Introdução

No desafio proposto pelo professor da disciplina de Projeto e Tópicos Especiais em Projeto de Sistemas de Software, André Santanchè, do Curso de Pós-Graduação em Engenharia de Software da Unifacs, de implementar um programa utilizando Padrões de Projeto ou um Estilo Arquitetural, possuíamos uma idéia do que poderíamos implementar. Uma vez que um dos integrantes da equipe pratica boliche, resolvemos implementar um protótipo de um simulador de jogadas de Boliche. Este programa, desenvolvido em Java, é composto de 3 classes e implementa o padrão Observer. Nas seções seguintes, descrevemos o padrão, o conceito e o código do sistema.

2.  O padrão Observer

“O padrão Observer define a dependência um-para-muitos entre objetos para que quando um objeto mude de estado todos os seus dependentes sejam avisados e atualizados automaticamente.” [Gamma,98]

Conforme definição de Erich Gamma, dado um conjunto de objetos, alguns destes terão atributos que definirão o seu estado. Neste caso é caracterizada a relação: Observado (Subject) X Observador (Observer). O primeiro se comporta definindo seu estado e o segundo será avisado dos estados do primeiro.

 diagrama_do_padrao

 

O padrão também é conhecido por: Dependents, Publish-Subscribe

3.  O BowlingObserver

Levamos em consideração o formato de um jogo de boliche, que basicamente é constituído de Jogadores, Pista, Bola e Pinos, e que seu funcionamento é baseado em jogadas, feita por um jogador através de arremesso de uma bola numa pista em direção à 10 pinos. O objetivo seria derrubar estes pinos.

 A partir do conceito de funcionamento do Observer, consideramos que o padrão seria o que mais se adequaria ao modelo implementado, uma vez que  temos a figura dos pinos que possuem os estados “derrubados” e “em pé”, e o jogador, autor da jogada, como um observador que contará pontos conforme os estados dos pinos após um lançamento.

Sujeito x Observador

No modelo implementado, consideramos o objeto observado, ou Sujeito, a própria pista. Isto porque, apesar de os pinos possuírem os seus estados, é ela que contém como atributo o seu próprio conjunto de pinos, e consequentemente sofre a jogada. Neste caso, será ela a responsável por alertar ao Observador o estado de cada um dos seus dez pinos.

Num jogo de boliche, um jogador pode efetuar jogadas em mais de uma pista. Sendo assim, a classe pista, poderá ser instanciada “n” vezes, o que permite que o jogador observe os estados de pinos de todas as pistas em que ele efetuou a jogada.

uc

4.  Implementação do BowlingObserver

O programa foi desenvolvido em JAVA, primeiramente devido ao domínio da  linguagem pela equipe e também porque o JAVA oferece, no seu pacote java.util, estruturas que possibilitam implementação do padrão Observer, que são a classe Observable e a interface a Observer.

Foram então implementadas as classes descritas na seção anterior, Pinos, Pista e Jogador. Sendo que Pista é uma classe herdeira de Observable, que contém como atributos uma serie de Pinos, e Jogador é uma classe que implementa a interface Observer. O diagrama de classes do programa é descrito na figura 3.

 diagrama

 

Pista – O Observable

Nesta seção demonstramos a implementação da classe Pista, conforme o diagrama da fig. 4

 

 

 pista

A classe é descrita como public class Pista extends Observable possui os dez atributos do tipo Pino. Estes são inicializados na própria declaração deles.

 O método jogarBola(List<Integer> pinos) receberá uma lista de inteiros representando os números dos pinos derrubados. Caso esta lista esteja vazia, significa que ou nenhum pino foi derrubado, ou se trata da arrumação inicial  dos mesmos.

 public void jogarBola(List<Integer> pinos){         

            for(Integer p : pinos){

                  switch(p){

                  case (1):                   

                        this.pin1.setDerrubado(true);

                        break;

                  case (2):

                        this.pin2.setDerrubado(true);

                        break;     

                  case (3):

                          this.pin3.setDerrubado(true);

                          break;   

                  case (4):

                          this.pin4.setDerrubado(true);

                          break;   

                  case (5):

                          this.pin5.setDerrubado(true);

                          break;   

                  case (6):

                          this.pin6.setDerrubado(true);

                          break;   

                  case (7):

                          this.pin7.setDerrubado(true);

                          break;   

                  case (8):

                          this.pin8.setDerrubado(true);

                          break;   

                  case (9):

                          this.pin9.setDerrubado(true);

                          break;

                  case (10):

                          this.pin10.setDerrubado(true);

                          break;                              

                  }          

            }

            derrubouPinos();

      }

 

Ao final deste método é evocado o método derrubouPinos(). Este método é o mais importante para o funcionamento do padrão, pois é ele quem vai alertar ao Observer os novos estados dos Pinos.

 

public void derrubouPinos() {

      setChanged(); //indica que o estado do objeto mudou.

notifyObservers();//Notifica ao observer que o estado do Objetos mudou

      }

 

Através do método displayPista() é impressa no console a organização dos pinos conforme os seus estados. Caso, o pino esteja derrubado, ele não será impresso no conjunto.

 

public void displayPista(){

String display = getPin7().getNumber()+” “+getPin8().getNumber()+” “+getPin9().getNumber()+” “+getPin10().getNumber()+” ” +”\n”          

+” ” +getPin4().getNumber() + ” ” + getPin5().getNumber()+ ” ” + getPin6().getNumber()+ “\n”

      +”  “+ getPin2().getNumber()+” “+ getPin3().getNumber()+” “+”\n”

      + ”   “+getPin1().getNumber();

           

            System.out.println(display);

 

/* 7 8 9 10

 *  6 5 4

 *   3 2

 *    1

 */

 

      }

     

O que determina se o Pino será impresso ou não, é o valor do seu atributo, juntamente com o comportamento do método getNumber().

 

public String getNumber() {

          if(isDerrubado()){

                return ” “;

          }else{

                return number;

          }

 

Os métodos setChanged() e notifyObservers() são métodos da superclasse Observable (fig. 3).

Jogador – O Observer

A classe Jogador descrita como public class Jogada implements Observer é demonstrada na fig. 5.

jogador

Um dos métodos mais importantes para o funcionamento do Observer é o próprio construtor da classe. É nele que vai ser determinado qual é o objeto observable.

 public Jogador(Observable observable){

       this.observable = observable;

       observable.addObserver(this);

}

 

Pode-se notar que o atributo observable recebe o parâmetro observable do construtor, bem como adiciona uma instância de Jogador na sua lista de Observers através do método addObserver(Observer o).

 O resultado do funcionamento do padrão, esta justamente no método update().  Este método é disparado através da chamada do notifyObservers(). Outra possibilidade para disparar o update(), seria configurar o próprio Sujeito para chamar o notify() após uma mudança de estado. No entanto a vantagem deste optado é que a chamada do notify() pode ser executada após todas a mudanças executadas conforme o objetivo do negócio [Gamma,98].

 public void update(Observable obs, Object arg ) {

            if ( obs instanceof Pista ) {

                  Pista p = (Pista) obs;            

                  p.displayPista();

             }

 

Dentro do update() instanciamos um objeto Pista e chamamos o método displayPista(), desta forma visualizaremos no console, após as mudanças de estado, os pinos que estão “em pé”. Mais a frente, poderemos notar que este método (update()) não será chamado em nenhum momento no programa e sim implicitamente através do notifyObservers().

 

A execução do programa

Para executar o programa implementamos o método main na própria classe Jogador. Esta opção foi feita apenas para comprovar o funcionamento do programa, o que posteriormente deverá ser realocado para outra classe.

 

public static void main(String args[]){

            List<Integer> pinos= new ArrayList<Integer>(0);           

            Pista pista = new Pista();        

            Jogada j = new Jogada(pista);

            System.out.println(“PISTA PRONTA”);

            System.out.println(“##############”);

            pista.jogarBola(pinos);

            System.out.println(“##############”);

            System.out.println(“BOLA LANÇADA…”);  

            System.out.print(“PINOS DERRUBADOS : “);

            for(String s : args){

                  System.out.print(s+” “);

                  Integer pino = new Integer(s);

                  pinos.add(pino);

            }

            System.out.println(“”);

            System.out.println(“PISTA COM PINOS RESTANTES”);

            System.out.println(“##############”);

            pista.jogarBola(pinos);

            System.out.println(“##############”);                     

 

      }

 

Neste código, iniciamos a jogada através da primeira chamada de pista.jogarBola(pinos). Como a lista pinos está vazia, o programa posiciona os pinos na ordem antes da jogada.

Após “BOLA LANÇADA” e “PINOS DERRUBADOS:”, percorremos a lista parâmetros através de um for e preenchemos a lista pinos. Ao executar novamente pista.jogarBola(pinos) é impressa a nova organização dos pinos após a jogada.

É importante notar que no método jogarBola() não possui em seu código a chamada para o método displayPista(), no entanto a pista é impressa mesmo após a jogada. Isso se deve a chamada de derrubouPinos() que chama os métodos setChanged() e notifyObservers(). Este último disparará o método update() que possui a chamada para displayPista() e produzirá o resultado demonstrado na figura 6.

tela

5. Vídeo

6.  Referências

Gamma, E. Helm, R. Johnson, R. Vlissides, J. – Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1998.

 http://www.dsc.ufcg.edu.br/~jacques/cursos/map/html/arqu/observer.htm.

 Acessado em 03 de junho de 2009

 MarioSAM, Design Patterns – Padrão Observer - 05 dezembro 2008 as 2:33

http://www.mariosam.com/designpatterns/design-patterns-padrao-observer .

Acessado em 03 de junho de 2009

Posted in Padrão Observer | 1 Comment »

 
Seguir

Obtenha todo post novo entregue na sua caixa de entrada.