Lab3 Douglas Bokliang

aluno: Douglas Bokliang Ang Cunha
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 14/10/2008 (3)

Introdução

Neste laboratório desenvolvemos uma biblioteca de máquina de estados, aplicando esta em dois exemplos mostrados mais adiante. Para concluir este trabalho foi necessário modelar o problema usando Orientação Objeto, utilizar dois diferentes tipos de padrões (Observer e Command) e ainda aprender a utilziar classes anônimas.

Desenvolvimento

Para desenvolver a biblioteca de máquina de estados, foi utilizado as seguintes classes:
- StateMachine
Principal classe do pacote, onde fica definida a interface pública da máquina de estados. Entre outros, possui os métodos para criar um estado e criar um evento, métodos essenciais na aplicação da biblioteca para uma determinada máquina.

package stateMachine;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Vector;

public class StateMachine {
    private State currentState;
    private Dictionary<String, State> states = new Hashtable<String, State>();
    private Dictionary<String, Event> events = new Hashtable<String, Event>();
    private Vector<Observer> observers = new Vector<Observer>();

    public void registerObserver(Observer observer){
        observers.add(observer);
    }

    protected void notifyObservers(){
        for(Observer obs : observers){
            obs.update(this, currentState.name);
        }
    }
    //métodos para a definição da máquina
    public void createState(String name, StateCommand onEnter){
        State s = new State(name);
        s.command = onEnter;
        states.put(name, s);
    }

    public void createState(String name){
        createState(name, null);
    }

    public void setInitialState(String name){
        currentState = states.get(name);
    }

    public void createEvent(String name, String[] from, String to, EventCommand onSuccess){
        Event e = new Event(name, this);
        for(String str : from){
            e.from.add(str);
            e.to.add(to);
        }
        e.command = onSuccess;
        events.put(name,e);
    }

    public void createEvent(String name, String[] from, String to){
        createEvent(name, from, to, null);
    }

    public void createEvent(String name, String from, String to,  EventCommand onSuccess){
        String[] array = new String[1];
        array[0] = from;
        createEvent(name, array, to, onSuccess);
    }

    public void createEvent(String name, String from, String to){
        createEvent(name, from, to, null);
    }

    public void appendTransitionToEvent(String event, String fromState, String toState){
        String[] array = new String[1];
        array[0] = fromState;
        appendTransitionToEvent(event, array, toState);
    }

    public void appendTransitionToEvent(String event, String[] fromState, String toState){
        Event e = events.get(event);
        for(int i = 0; i < fromState.length; i++){
            e.from.add(fromState[i]);
            e.to.add(toState);
        }
    }

    public void setGuardToEvent(String event,EventGuard g){
        Event e = events.get(event);
        e.guard = g;
    }
    // métodos para a execução da máquina
    public boolean fireEvent(String name) throws UndefinedTransitionException{
        boolean fired = true;
        Event e = events.get(name);
        if (e == null) throw new UndefinedEventError("Undefined Event Error");
        if(e.guard != null && !e.guard.shouldPerform()) 
            fired = false;
        else{
            State s = Transition(name);
            if(s == null) throw new UndefinedTransitionException("Undefined Transition Exception");
            else{
                String previousState = currentState.name;
                currentState = s;
                notifyObservers();
                if(e.command != null) 
                    e.command.execute(previousState);
                if(currentState.command != null) 
                    currentState.command.execute();
            }
        }
        return fired;
    }

    public String getCurrentStateName(){
        return currentState.name;
    }

    private State Transition(String event) {
        Event e = events.get(event);
        State newState = null;
        for(String str : e.from){
            if(str.equals(currentState.name)) {
               String state = e.to.get(e.from.indexOf(str));
               newState = states.get(state);
            }
        }

        return newState;
    }
}

-State e StateCommand
Na classe State fica definido os parâmetros que um estado deve ter, e a classe abstrata StateCommand é responsável por chamar o método execute(), o qual tem seu corpo definido através de classes anônimas.

-Event e EventCommand
A classe Event não apenas possui os parâmetros de um evento, mas também determina todas as transições de estados que tal evento pode realizar. Isto é feito através de dois Vectors, um representando os estados de saída e um representando os estados de entrada, sendo que quando os índices dos dois são iguais quer dizer que o evento pode levar do respectivo estado de saída para o respectivo estado de entrada.
A classe abstrata EventCommand tem a mesma função para os eventos que a StateCommand tem para os estados.

-EventGuard
Interface associada a classe Event. Quando há uma condição de guarda necessária para que um evento ocorra (condição implementada em setGuardToEvent() da classe StateMachine através de classes anônimas), esta é verificada através do método shouldPerform(), sendo que se este retornar um valor falso, o evento não ocorre.

-Observer
Interface responsável por atualizar o novo estado no qual a máquina se encontra para todos os observadores. Interface relacionada com o padrão Observer.

-UndefinedTransitionException
Quando um evento não está determinado para um certo estado atual, o método fireEvent() de StateMachine deve lançar esta exceção.

Obs: Apenas coloquei o código da classe StateMachine nesta parte do relatório por considerar a mais indispensável pro relatório, as demais podem ser vistas no arquivo em anexo.

Teste

Após terminar de implementar a biblioteca da máquina de estados, o teste fornecido nas intruções do laboratório foi executado. Eis o resultado:

test.jpg

Exemplo1: Professor e Turma (fornecido nas instruções)

Para testar o exemplo fornecido nas instruções, foi criado a classe NewMain a seguir para verificar se as transições estavam ocorrendo como o esperado. Veja o código desta classe abaixo:

package teste;
 
public class NewMain {
 
    public static void main(String[] args) {
        Professor prof = new Professor();
        Turma t = new Turma();
 
        t.registerObserver(prof);
        t.fire("cancerizar");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("dormir");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("ser_atenta");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("usar_note");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("dormir");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("dormir");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("cancerizar");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("ser_atenta");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("cancerizar");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        prof.setGanhandoMilhoesDeDolares(true);
        t.fire("dormir");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
 
        t.fire("usar_note");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva()+"\n");
    }
}

A execução deste main gerou o seguinte output esperado:

resultado1.jpg

Exemplo2: AlunoITA

Em seguida, foi feito uma outra aplicação da máquina de estados criada. A idéia deste exemplo foi fazer uma simulação simplificada do cotidiano de um aluno do ITA, no qual existem vários eventos, deixando o aluno transitar entre alguns estados. Veja o diagrama de estados:

diagrama

Foi criado novamente uma classe para fazer a simulação do exemplo. Esta classe está especificada abaixo:

package exemplo;
 
import stateMachine.*;
 
public class NewMain {
 
    public static void main(String[] args) {
        AlunoITA a = new AlunoITA();
 
        try{
            a.fireEvent("acordar");
            a.fireEvent("ir para aula");
            a.fireEvent("ir para lab");
            a.fireEvent("dormir");
            a.fireEvent("acordar");
            a.fireEvent("tomar red bull");   //nao deve fazer nada, nao tem prova amanha
            a.fireEvent("aula de CES22");
            a.fireEvent("fazer prova");
            a.fireEvent("terminar prova");
            a.setTemProvaAmanha(true);
            a.fireEvent("dormir");   //nao deve dormir, tem prova amanha
            a.fireEvent("tomar red bull");
            a.fireEvent("ir para aula");
            a.fireEvent("fazer prova");
            a.setTemProvaAmanha(false);
            a.fireEvent("terminar prova");
            a.fireEvent("dormir");
 
        }catch(UndefinedTransitionException e){
            System.out.println("ERRO: trasição não definida: " + e.getMessage());
        }
    }
}

Após executar o main, obteve-se o seguinte resultado esperado:

resultado2.jpg

Conclusão

Este laboratório se mostrou muito complicado no início. Demorei diversas horas para começar a entender o que estava acontecendo, o que cada método deveria fazer, como implementar etc. No entanto, depois de ler várias vezes as intruções do lab, estudar o padrão Command no Design Patterns e pedir um pouco de ajuda com os amigos, o trabalho começou a sair e se mostrou não ser tão complicado quanto parecia.
No fim das contas, acredito que este lab foi importante principalmente por ter sido o primeiro no qual a gente precisou se virar mais por conta própria, teve menos informações fornecidas em relação aos outros. Além disso, foi possível utilizar dois padrões de projetos e aprender sobre classes anônimas.

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