Lab3 Marcus Edson

aluno: Marcus Edson Barreto Brito
ano/sem: 2008/2o.
data do laboratório (2a. semana) : 09/10/2008 (2)

Introdução

Nesse laboratório foi desenvolvida uma biblioteca para a criação de máquinas de estado finitas movidas a evento. Pôde-se utilizar conceitos aprendidos em aula, como o padrão "Observer", o uso de classes anônimas e o tratamento de exceções. A seguir testamos nossa biblioteca com a criação da máquina de estados vista no laboratório,em que um professor observa o comportamento da turma e muda seu estado de acordo com as ações da turma. A seguir criamos uma nova máquina de estados que está especificada mais abaixo.

Desenvolvimento

O diagrama de Classes da biblioteca MaqEstados é mostrada abaixo:

lab3_fig1.JPG

Classe State

A classe State é a classe que será instanciada ao criarmos um novo estado na nossa máquina de estados.
Ela possui uma variável do tipo StateCommand, que é inicialmente setada como "null", mas que pode receber um outro valor com a chamada de método setStateCommand. O método getStateCommand retorna o state command associado a determinado estado.

package MaqEstados;
 
public class State{
 
    private String estado;
    private StateCommand sc = null;
 
    State(String estado){
        this.estado=estado;
    }
 
    public void setStateCommand(StateCommand statCom){
        this.sc=statCom;   
    }
 
    public String getStateName(){
        return estado;
    }
 
    public StateCommand getStateCommand(){
        return sc;
    }
}

Classe Event

A classe Event é a classe que será instanciada ao criarmos um novo evento. Cada evento terá dois vetores de estados que guardarão as possíveis transições para ele. No primeiro vetor guardamos os possíveis estados de saída e no segundo o estado de chegada correspondente para esse evento. Dois estados relacionados por um evento terão a mesma posição nos dois vetores, o que será garantido pelo método novaTransicao.
O método mudaEstado recebe o estado atual da máquina e retorna o estado para o qual ela deve ir. Se a transição não existir ou o evento de guarda for falso ela retorna null.
Essa classe ainda possui uma variável do tipo EventCommand, que é setada como nula e pode receber um novo valor usando-se o método setStateCommand.

package MaqEstados;
import java.util.*;
 
public class Event{
    private String evento;
    private EventCommand ec=null;
    private EventGuard eg=null;
 
    //Os estados final e inicial de uma nova transição são colcoados
    //em dois vetores diferentes e ficam na mesma posição, já que são
    //acrescentados ao mesmo tempo pelo método "novaTransicao"
    private Vector <State> estado_inic = new Vector <State>();
    private Vector <State> estado_final = new Vector <State>();
 
    Event(String event){
        this.evento=event;
    }
 
    void novaTransicao(State est_inic, State est_final){
        estado_inic.add(est_inic);
        estado_final.add(est_final);
    }
 
    State mudaEstado(State oldState){
        int i=estado_inic.indexOf(oldState);
 
        if(eg==null){
            if(i!=-1){
                return estado_final.get(i);
            }
            else{
                return null;
            }
        }
        else{
        if (i!=-1 && eg.shouldPerform()){
            return estado_final.get(i);    
        }
        else
            return null;
        }
    }
 
    public void setEventGuard(EventGuard eg){
        this.eg=eg;
    }
 
    public void setEventCommand(EventCommand evetCom){
        this.ec=evetCom;   
    }    
 
    public EventCommand getEventCommand(){
        return ec;
    }
 
    String getEventName(){
        return evento;
    }
 
}

Exceções

Temos 3 classes de exceções que estendem as superclasses Exception e RuntimeException.
Essas classes são:
UndefinedEventException: lança exceção para o caso em que se chama um evento que não existe.
UndefinedStateException: lança exceção para o caso em que tenta-se chegar em um estado inexistente
UndefinedTransitionException: lança exceção para o caso em que se tenta realizar uma transição inexistente
Abaixo o código de UndefinedStateException. O código das outras duas classes é análogo, sendo que UndefinedEventException é não checada e UndefinedTransitionException é checada.

package MaqEstados; 
 
public class UndefinedStateException extends RuntimeException{
 
    public UndefinedStateException(){
        super();    
    }    
 
    public UndefinedStateException(String str) {
        super(str);
    }
 
    public UndefinedStateException(String str, Throwable cause){
        super(str, cause);
    } 
 
    public UndefinedStateException(Throwable cause){
        super(cause);
    } 
}

Classe StateMachine

A classe stateMachine foi implementada de acordo com as especificações das instruções para o lab.
Essa classe possui 3 vetores que guardam os estados, os eventos e os observadores criados.
Possui também uma variável que guarda o estado atual da máquina de estados.
Comentários sobre os métodos foram feitos no próprio código:

package MaqEstados;
import java.util.*;
 
public class StateMachine{
 
    private State estadoAtual;
    private Vector <Observer> obs = new Vector <Observer>();
    private Vector <State> estados = new Vector <State>();
    private Vector <Event> eventos = new Vector <Event>();
    //comportamento de observável
    public void registerObserver(Observer observer){
        obs.add(observer);
    }
 
    protected void notifyObservers(){
        for(Observer aux : obs){
            aux.update(this, estadoAtual.getStateName());
        }
    }
 
    //Procura no vetor estados um estado com o nome passado como parâmetro da função.
    //No caso de o estado não existir o método lança uma exceção. Esse lançamento de exceção
    //será útil para os métodos que irão utilizar findState e que precisarão lançar exceção no 
    //caso de o estado não estar definido.
    public State findState(String nome) throws UndefinedStateException{
        for ( State aux : estados ){
            if(aux.getStateName().equals(nome)){
                return aux;
            }
        }
        throw new UndefinedStateException("Estado não definido");
    }
 
    //Análogo ao findState, mas procura no vetor eventos
    public Event findEvent(String nome) throws UndefinedEventException{
        for( Event aux : eventos){
            if(aux.getEventName().equals(nome)){
                return aux;
            }    
        }
       throw new UndefinedEventException("Evento não definido");
    }
 
    //métodos para a definição da máquina
    //Métodos que criam um novo estado e o colocam no vetor estados
    public void createState(String name, StateCommand onEnter){
        State estado = new State(name);
        estado.setStateCommand(onEnter);
        estados.add(estado);    
    }
 
    public void createState(String name){
        State estado = new State(name);
        estados.add(estado);
    }
 
    //Define o estado atual da máquina
    //Observe que se o estado passado não existir será lançada 
    //uma exceção devido ao métdo findState 
    public void setInitialState(String name){
        estadoAtual=findState(name);
    }
 
    //Métodos que criam um novo evento e o colocam no vetor eventos
    //Esses métodos lançarão exceção no caso em que tentarmos criar um evento que tem um estado de saída indefinido.
    //No entanto, o evento é criado mesmo que o estado de chegada não exista. 
    //A exceção de será lançada quando tentarmos disparar o evento e o estado de chegada ainda não tiver sido criado. 
    public void createEvent(String name, String[] from, String to, EventCommand onSuccess){
        Event evento =  new Event(name);
        for(String aux : from){
            evento.novaTransicao(findState(aux), new State(to));    
        }
        evento.setEventCommand(onSuccess);
        eventos.add(evento);    
    }   
 
    public void createEvent(String name, String[] from, String to){
        Event evento =  new Event(name);
        for(String aux : from){
            evento.novaTransicao(findState(aux), new State(to));    
        }
        eventos.add(evento);
    }
 
    public void createEvent(String name, String from, String to,  EventCommand onSuccess){
        Event evento = new Event(name);
        evento.novaTransicao(findState(from), new State(to));
        evento.setEventCommand(onSuccess);
        eventos.add(evento);
    }
 
    public void createEvent(String name, String from, String to){
        Event evento = new Event(name);
        evento.novaTransicao(findState(from), new State(to));
        eventos.add(evento);
    }
 
    //Faz com que uma nova transição se relacione a um envento já criado.
    //Nesse caso osmétodos findState e findEvent lançarão exceção se o evento não existir ou
    //se os estados de chegada ou saída não existirem 
    public void appendTransitionToEvent(String event, String fromState, String toState){
        Event evento = findEvent(event);
        evento.novaTransicao(findState(fromState), findState(toState)); 
    }
 
    public void appendTransitionToEvent(String event, String[] fromState, String toState){
        Event evento =  findEvent(event);
        for(String aux : fromState ){
            evento.novaTransicao(findState(aux), findState(toState));    
        }
    }
 
    //Define um evento de guarda para determindo evento.
    //Lança exceção caso o evento não exista
    public void setGuardToEvent(String event,EventGuard g){
        Event evento = findEvent(event);
        evento.setEventGuard(g);
    }
 
    //métodos para a execução da máquina
    //Método que realiza o lançamento de um evento e faz a mudança de estado da máquina
    //Caso a transição não possa ser efetuada esse método lança uma exceção
    public boolean fireEvent(String name) throws UndefinedTransitionException , UndefinedEventException, UndefinedStateException { 
        boolean fire=true;
 
        Event evento = findEvent(name);
        State novoEstado = evento.mudaEstado(estadoAtual);
        State previousState = estadoAtual;
        //Primeiro trata o caso em que não existe a transição, ou seja, quando tentamos mudarEstado
        //obtivemos null como retorno, o que significa que uma transição saindo do estadoAtual não está
        //definida, ou então que a cláusula de guarda está setada para que não seja realizado o evento
        if( novoEstado==null ) { //testar if(novoEstado==null)  !(novoEstado instanceof State)
            fire=false;
            throw new UndefinedTransitionException("Transicao não definida!");
        }
        //Se o estado retornado não foi nulo, verificamos se esse é um estado definido no nosso vetor de estados
        else{
            fire=false;
            for ( State aux : estados ){
                if(aux.getStateName().equals(novoEstado.getStateName())){
                    fire=true;
                }
            }
            if(fire==false){
                throw new UndefinedStateException("Estado final não definido");
            }
 
        }
        fire=true;
        setInitialState(novoEstado.getStateName());
        notifyObservers();
        if(evento.getEventCommand()!=null){
            evento.getEventCommand().execute(previousState.getStateName());
        }
        if(novoEstado.getStateCommand()!=null){
            novoEstado.getStateCommand().execute();
        }
 
        return fire;  
    }
 
    //Retorna o nome do estado em que a máquina se encontra no momento
    public String getCurrentStateName(){
        return estadoAtual.getStateName();
    }
 
}

Testes

Resultados retornados pela classe teste:
lab3_teste_fig2.JPG

Exemplo de aplicação dado em sala

Testes realizados com a implementação do exemplo dado em sala, em que temos um professor observando o comportamento de uma turma e mudando o seu estado de acordo:

lab3_teste2_fig3.JPG
Primeiramente criamos um professore uma turma e colocamos o professor como observador dessa turma.
A seguir a turma disparou o evento "dormir":
lab3_teste2_fig4.JPG
Depois a turma disparou o evento "cancerizar":
lab3_teste2_fig5.JPG
Colocamos isGanhandoMilhoes como true e disparamos o evento "usar_note". Obtemos um UndefinedEventException, pois o professor não pode ficar desmotivado se ganha milhões:
lab3_teste2_fig6.JPG
Voltamos isGanhandoMilhoes para false e disparamos o evento dormir duas vezes:
lab3_teste2_fig7.JPG
Depois disparamos o evento "ser_atenta":
lab3_teste2_fig8.JPG

Exemplo de aplicação escolhido

Para realização de mais testes com um exemplo diferente foi criada a seguinte máquina de estados:
lab3_imp_fig13.JPG
Nela temos um semáforo que muda dessa forma: Vermelho->Verde->Amarelo->Vermelho através de um único evento.
Temos uma classe pedestre que observa o estado do sinal e de acordo com o seu estado ela atravessa a rua.
Adicionamos uma cláusula de guarda, na qual o pedestre não atravessa a rua se estiver cansado.

Mostremos os testes realizados:
Variamos o estado do sinal para podermos ver todas as respostas do pedestre às mudanças do sinal.
lab3_imp_fig9.JPG
lab3_imp_fig10.JPG
lab3_imp_fig11.JPG
Para esse caso colocamos a cláusula de guarda como verdadeira, ou seja, o pedestre está cansado,e portanto, não atravessará a rua mesmo que o sinal fique verde:
lab3_imp_fig12.JPG

Projeto no BlueJ: CES22_lab3

Conclusão

Nesse laboratório foi criada uma biblioteca para implementação de máquinas de estado. Para mim ele foi bastante proveitoso, pois pude pôr em prática e consolidar conceitos vistos em aula como o padrão Observer, que até então estava obscuro para mim, e as classes anônimas, além de ter me proporcionado uma maior familiaridade e segurança com a linguagem Java e programação orientada a objeto em geral e uma percepção da utilidade de se criar bibliotecas para uso futuro. Com certeza o que aprendi vai ajudar a iniciar a realização do projeto para o exame. No entanto, tempo de realização dele foi muito longo, e eu sugeriria que, ao invés de dar mais tempo para a entrega de um lab como esse, ele fosse dividido em dois menores.

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