Lab3 Leonardo Bruno

aluno: Nome do Aluno
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 06/08/2008 (2)

Introdução

Neste laboratório podemos implementar vários conceitos visto nas aulas teóricas, podendo assim entendê-los melhor, através de uma aplicação prática desenvolvida por nós. Trabalhamos novamente com padrões de projetos, com exceções e com os vários conceitos envolvidos na orientação a objeto que já estão tendo uma compreensão amplamente satisfatória. Vamos realizar a implementação de uma máquina de estados determinística aplicável a diversas situações.

Desenvolvimento

Abaixo podemos perceber o código da classe StateMachine que possuirá como varáveis de instância o estado autal da máquina, algumas variáveis auxiliares de evento e estado, e três listas. A primeira é a lista dos possíveis estados da máquina, a segunda é uma lista dos possíveis eventos que podem ocorrer nesta máquina e a terceira é a lista de todos os observadores desta máquina. A máquina terá todos os metódos solicitados, sendo capaz de :

  1. notificar os observadores - notifyObservers()
  2. registrador um observador na lista de observadores - registerObserver(Observer observer)
  3. criar um estado possível para máquina adicionado este a respectiva lista
  4. setar um estado inicial à maquina
  5. criar um evento
  6. adicionar possíveis trasições a um determinado evento
  7. criar uma condição de guarda para um determinado evento
  8. disparar um determinado evento, caso seja possível
  9. retornar o atual estado da máquina

Internamente também foram criados dois métodos auxiliares que irão agir dentro dos métodos públicos citados acima

  1. Procurar um determinado estado na lista da máquina
  2. Procurar um determinado evento na lista da máquina
public class StateMachine
{
    // Variáveis de Instancia
    private ArrayList<Estado> estados = new ArrayList<Estado>();
    private ArrayList<Evento> eventos = new ArrayList<Evento>();
    private ArrayList<Observer> observadores = new ArrayList<Observer>();
    private Estado estadoMaquina,aux;
    private Evento eventoAux;
 
    //Construtor da Classe
    public StateMachine(){}
 
    //comportamento de observável
    public void registerObserver(Observer observer) {
        observadores.add(observer);
    } 
    protected void notifyObservers() {
        String nomeEstado = getCurrentStateName();
        for(Observer obs : observadores) {
            obs.update(this, nomeEstado);
        }
    }
 
    //métodos para a definição da máquina
 
    //método que cria um estado com um nome e um StateCommand
    public void createState(String name, StateCommand onEnter) 
    {
        Estado est = new Estado(name,onEnter);
        estados.add(est); 
    }
    //método que cria um estado apenas com um nome
    public void createState(String name)
    {
        Estado est = new Estado(name);
        estados.add(est); 
    }
    //método que seta um estado inicial à máquina
    public void setInitialState(String name) 
    {
        aux = procurarEstado(name);
        if(aux != null) estadoMaquina = aux;          
    }
 
    //método que procura um estado pelo nome na lista de estados da máquina,
    //tratando a exceção quando não houver um estado com o nome fornecido
    private Estado procurarEstado(String nome) throws UndefinedStateException
    {
         for(Estado est : estados) if(est.getNome().equals(nome))  return est;
         throw new UndefinedStateException();        
         //return null;
    }
 
   //metodo que cria um evento com vários estados de origem e com EventCommand 
   //O evento eh adicionado na lista de eventos da maquina
    public void createEvent(String name, String[] from, String to, EventCommand onSuccess)
    {
        Evento even = new Evento(name,onSuccess);
        try{
        for(String n : from) {
            even.addTransicao( procurarEstado(n), procurarEstado(to) );
        }
         eventos.add(even);
        }catch(UndefinedStateException e){}; 
    }
   //metodo que cria um evento com vários estados de origem e sem EventCommand   
   //O evento eh adicionado na lista de eventos da maquina
    public void createEvent(String name, String[] from, String to)
    {
        Evento even = new Evento(name);
        try{
        for(String n : from) {
            even.addTransicao( procurarEstado(n), procurarEstado(to) );
        }
         eventos.add(even);   
        }catch(UndefinedStateException e){}; 
    }
 
   //metodo que cria um evento com um estado de origem e com EventCommand     
   //O evento eh adicionado na lista de eventos da maquina
   public void createEvent(String name, String from, String to,  EventCommand onSuccess)
    {
        Evento even = new Evento(name,onSuccess);
        try{
        even.addTransicao(procurarEstado(from), procurarEstado(to));
        eventos.add(even);
        }catch(UndefinedStateException e){}; 
    }    
 
   //metodo que cria um evento com um estado de origem e sem EventCommand 
   //O evento eh adicionado na lista de eventos da maquina
    public void createEvent(String name, String from, String to)
    {
        Evento even = new Evento(name);
        try{
        even.addTransicao( procurarEstado(from), procurarEstado(to));
        eventos.add(even); 
        }catch(UndefinedStateException e){}; 
    }    
 
    //metodo que procura um evento na lista da maquina, tratando a exececao qnd este nao existir
    private Evento procurarEvento(String n) throws UndefinedEventException
    {
         for(Evento e : eventos) if(e.getNome().equals(n))  return e;
         throw new UndefinedEventException();        
         //return null;       
    }
 
    //este metodo irá adicionar uma trancisao a um determinado evento
    public void appendTransitionToEvent(String event, String fromState, String toState) throws UndefinedTransitionException
    {
        try {
            eventoAux = procurarEvento(event);
            eventoAux.addTransicao(procurarEstado(fromState),procurarEstado(toState));
        } catch (Exception e) {throw new UndefinedStateException();}
 
    }
 
    //este metodo irá adicionar várias trancisoes a um determinado evento
    public void appendTransitionToEvent(String event, String[] fromState, String toState) throws UndefinedTransitionException
    {
       try{ 
        eventoAux = procurarEvento(event);
        for(String n : fromState) 
              eventoAux.addTransicao(procurarEstado(n),procurarEstado(toState));           
 
            }catch (Exception e) {throw new UndefinedStateException();}
    }
 
    public void setGuardToEvent(String event,EventGuard g)
    {
       try{
           eventoAux = procurarEvento(event);
           eventoAux.setEventGuard(g);
       }catch (UndefinedEventException e) {}         
    }
 
    // métodos para a execução da máquina
    public boolean fireEvent(String name) throws UndefinedTransitionException
    {
        boolean mudou=false; //essa variável irá indicar se houve ou não mudança de estado na máquina
        try {                                                      
         eventoAux = procurarEvento(name);  //pode ser lançada uma exceção que será tratada a seguir
        if(eventoAux.getEG()) //podemos obsevar a condição de guarda atuando
        {
            aux=eventoAux.getProximoEstado(estadoMaquina);
 
            if(estadoMaquina!=aux&&aux!=null) 
            {                 
                eventoAux.execucaoEvento(estadoMaquina.getNome());
                estadoMaquina = aux;
                notifyObservers();
                estadoMaquina.execucaoEstado(); 
                mudou = true;                
            }
 
        }        
        else throw new UndefinedTransitionException(); //caso a condição de guarda não seja aceita será lançada uma exceção
    }catch (UndefinedEventException e) {throw new UndefinedTransitionException();}
    return mudou;
    }
 
    public String getCurrentStateName()
    {
      return estadoMaquina.getNome();   
    }
 
}

No código acima podemos observar que algumas exceções foram lançadas. Por exemplo, quando algum método pede para procurar um determinado estado, existe a possibilidade daquele estado não existir naquela máquina, e nesse caso será lançada uma exceção
UndefinedStateException. Isto ocorre no método procurarEstado(String nome). Notemos que este método é privado e que ele recebe um nome que pode ser de um estado que pode não estar na lista da máquina. Também notemos que sempre que este método é chamado a exceção é tratada pelo try-catch. Algo muito semelhante ocorre no método que procura um evento na lista de eventos da máquina. Também podemos observar o encapsulamento presente em toda a máquina. Primeiramente todas as variáveis estão protegidas e só podem ser acessadas através dos métodos. Segundo, todo o funcionamento da máquina está totalmente encoberto, sendo mascarado pelos metódos de controle da máquina.

A seguir, podemos observar o diagrama de classes do pacote da máquina de estados:

fig1.JPG

No digrama de classes podemos observar a presença do padrão obsever e do padrão Command. Também podemos observar a estrutura geral da maquina. Foi criada uma classe geral chamada de StateMchine que conterá todos os métodos de acesso externo. Também criamos duas classes principais: Estado e Evento. Uma classe auxiliar foi criada, Transição, que conterá dois estados, um inicial e um final, representado a trasição que um evento pode causar. Assim, cada Evento tem uma lista de Transições possíveis.

Implementamos também a classe de testes, conforme representado no diagrama de estados. Todos os testes apresentaram sucesso, conforme vemos na figura a seguir:

fig2.JPG

A seguir, aplicamos a máquina de estados no exemplo fornecido que envolve um professor e uma turma. Criamos um pacote com a máquina de estados desenvolvida e adicionamos este pacote no exemplo citado. Podemos observar o diagrama na figura a seguir:

fig3.JPG

Realizamos uma série de teste para comprovar o bom funcionamento de toda a máquina, mas pela extensão, vamos representar apenas uma bateria de testes: Testamos a máquina aplicando os métodos mostrados a seguir:

fig4.JPG
fig5.JPG

A seguir testamos a condição de guarda:

fig6.JPG

tentamos desmotivar, e vimos que não foi possível

fig4.JPG


fig4.JPG

retiramos a condição de guarda e desmotivamos normalmente

fig7.JPG
fig4.JPG

O resultado de todo esse teste é apresentado na figura abaixo :

fig8.JPG

Também verificamos o estado do do professor através do método.

EXEMPLO DE LIVRE ESCOLHA

Por último, aplicamos em uma situação escolhida por nós.
Imaginemos um quarto onde moram dois estudantes. Inicialmente o quarto está arrumado e organizado, mas com o passar do tempo, cada vez que alguem estuda no quarto, papeis e livros são espalhados. Então o quarto fica desorganizado, mais ainda fica arrumado, pois visualmente parece haver alguma ordem, mas na verdade nimguem consegue encontrar nada. Caso alguem estude novamente, o quarto ficará desorganizado! Assim, para melhorar o ambiente de convivência, os estudantes podem arrumar o quarto ou organizar. Para organizar são necessárias duas arrumações seguidas! Então, implementemos esta situação:

Temos então a classe quarto:
Ressaltemos o uso da classe anônima no método setUP, na criação de eventos com StateCommand.

public class Quarto extends WithSM implements StateMachine.Observer,HabitanteObserver
{   
 
    public Quarto() {
        super();
        this.setUP();
        sm.registerObserver(this);        
        System.out.println("Meu estado agora é " + getState() );
    }
 
    private void setUP() {
 
        sm.createState("desarrumado");
        sm.createState("desorganizado");        
        sm.createState("h8", new StateMachine.StateCommand(sm) {    //uso de classe anonima
            public void execute() {
                System.out.println("Está Insuportável!!! Arrumando não dá, organize!!!");
            }
        });
        sm.createState("arrumado");
        sm.createState("organizado");
 
        sm.setInitialState("arrumado");
 
        String estado[] = {"h8","desorganizado","desarrumado" , "arrumado", "organizado"};
 
        //evento arrumar
        sm.createEvent("arrumar",estado[1] , estado[2]);
        sm.appendTransitionToEvent("arrumar", estado[2], estado[3]);
        sm.appendTransitionToEvent("arrumar", estado[3], estado[4]);
        //evento organizar       
        sm.createEvent("organizar",estado[1] , estado[3]);
        sm.appendTransitionToEvent("organizar", estado[0], estado[2]);
        sm.appendTransitionToEvent("organizar", estado[2], estado[4]);
        sm.appendTransitionToEvent("organizar", estado[3], estado[4]);
        sm.appendTransitionToEvent("organizar", estado[4], estado[4]);
        //evento desarrumar
        sm.createEvent("desarrumar",estado[4] , estado[3]);
        sm.appendTransitionToEvent("desarrumar", estado[3], estado[2]);
        sm.appendTransitionToEvent("desarrumar", estado[2], estado[1]);
        sm.appendTransitionToEvent("desarrumar", estado[1], estado[0]);
        //evento desorganizar
        sm.createEvent("desorganizar",estado[4] , estado[2]);
        sm.appendTransitionToEvent("desorganizar", estado[3], estado[1]);
        sm.appendTransitionToEvent("desorganizar", estado[2], estado[0]);
        sm.appendTransitionToEvent("desorganizar", estado[1], estado[0]);
 
        sm.setGuardToEvent("arrumar", new StateMachine.EventGuard() {
            public boolean shouldPerform() {
                if(sm.getCurrentStateName().equals("h8"))
                {
                    System.out.println("Impossível Melhorar!!!");
                    return false;
                }
                else return true;
            }
        } );
    }
 
    public void update(StateMachine.StateMachine sm, String newStateName) {
        System.out.println("Meu estado agora é " + newStateName);
    }
 
    public void updateFromHabitante(Habitante t) {
        if (t.getState() == "arrumando")
            this.fire("arrumar");
        if (t.getState() == "desarrumando")
            this.fire("desarrumar");
        if (t.getState() == "organizando")
            this.fire("organizar");
        if (t.getState() == "desorganizando")
            this.fire("desorganizar");
        //System.out.println("Meu estado agora é " + getState() );    
 
    }
 
}

E a classe habitante:

import StateMachine.*;
import java.util.Vector;
public class Habitante extends WithSM
{
    private Vector<HabitanteObserver> observers = new Vector<HabitanteObserver>();
 
    public Habitante() {
         super();
         sm.createState("arrumando");
         sm.createState("desarrumando");
         sm.createState("organizando");
         sm.createState("desorganizando");
         sm.createState("parado");         
         sm.setInitialState("parado");
         String from[] = {"parado","desorganizando","desarrumando","arrumando","organizando"};
         sm.createEvent("arrumar", from, "arrumando");
         sm.createEvent("desarrumar", from, "desarrumando");
         sm.createEvent("organizar", from, "organizando");
         sm.createEvent("desorganizar", from, "desorganizando");
         sm.createEvent("parar", from, "parado");
 
     }
 
    public void registerObserver(HabitanteObserver observer){
        observers.add(observer);
    }
 
    private void notifyObservers() {
        for(HabitanteObserver obs : observers) {
            obs.updateFromHabitante(this);
        }
    }
 
    public void fire(String event) {
        super.fire(event);
        notifyObservers();
    }
}

[[/code]]

Abaixo temos o diagrama de classes, com um quarto e um habitantes criados. Podemos observar que inicialmente o quarto está arrumado. Então, iremos fazer com que o habitante desarrume.

fig9.JPG

A seguir, podemos observar a mudança de estado do quarto e vamos tentar organizar.

fig10.JPG

Então, desorganizamos duas vezes, e o quarto chegou no estado insuportável chamado h8, conforme vemos a seguir:

fig11.JPG

Tentamos arrumar mas não foi possível alterar o estado, só mesmo uma organizada deu jeito!

fig12.JPG

Assim, podemos comprovar o bom funcionamento do exemplo.

Conclusão

Assim, após este laboratório podemos compreender melhor o conceito de encapsulmanto, uma vez que implementamos, observamos e criamos situações que ilustram o uso e a importância do encapsulamento. Também exercitamos o padrão obsever e tivermos um primeiro contato prático com o padrão command. Também tivemos um primero contato com as classes anônimas, vendo na prática sua potencial importância. Outro ponto importante deste laboratório foi o tratamento de exceções, que ficou bem mais claro após este exercício. Por último, temos que ressaltar a alta aplicabilidade do programa desevolvido, o que tornou o lab bem mais interessante.

Add a New Comment
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License