Lab3 Igor Almeida

aluno: Igor de Sousa Almeida
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 06/08/2008 (2)

Introdução

Nesse laboratório foi desenvolvida uma biblioteca de máquina de estados, com o objetivo de firmar os diversos conceitos vistos em aula, o uso dos padrões Observer e Command, a utilização de classes anônimas e a aplicação de Orientação Objeto de uma forma mais prática.

Desenvolvimento

Conforme o esquema mostrado no roteiro do laboratório, deve-se criar uma biblioteca para máquinas de estados que funcionem da seguinte forma: dos vários estados existentes, existem eventos que ocasionam em alterações de estado e cada uma dessas alterações corresponde a uma transição.

Biblioteca stateMachine:

Essa biblioteca possui os requisitos necessários para o desenvolvimento de uma máquina de estados genérica. Para tanto foram implementadas as interfaces e classes abstratas especificadas no roteiro do laboratório: interface EventGuard e as classes abstratas EventCommand e StateCommand. A definição das mesmas é feita na aplicação através da utilização de classes anônimas.
Também já fora definida no roteiro a interface Observer responsável por observar alterações na classe StateMachine.
Além das classes já definidas, foi necessário elaborar mais outras para completar a biblioteca: uma classe StateMachine, cuja interface já fora definida no roteiro, classes State, Event e Transition e as classes para tratamento de excessões UndefinedStateException, UndefinedStateException e UndefinedEventException.


Classe State:
Classe representativa do estado. Possui como atributo o nome do estado e pode instanciar um objeto do tipo StateCommand contendo algum comando a ser executado quando esse estado é atingido. Essa classe possui dois contrutores, um recebe apenas uma String name como parâmetro, a outra também recebe um comando StateCommand.

package stateMachine;
 
class State {
    String name;
    StateCommand onEnter;
 
    protected State(String name){
        this.name = name;
    }
 
    protected State(String name, StateCommand onEnter){
        this.name = name;
        this.onEnter = onEnter;
    }
 
}

Classe Event:
Analogamente a classe State essa classe é representativa de um evento. Possui como atributos o nome do evento, o(s) nome(s) do(s) estado(s) que ele parte e o nome do estado que ele se destina. Pode instanciar um objeto do tipo EventCommand contendo algum comando a ser executado quando esse evento é disparado.
Na definição dos construtores um evento pode partir tanto de um quanto de vários estados diferentes, mas sempre o estado de chegada é único.

package stateMachine;
 
public class Event {
    String name;
    String from1;
    String[] from2;
    String to;
    EventCommand onSuccess;
 
    protected Event(String name, String from, String to){
        this.name = name;
        this.from1 = from;
        this.to = to;
    }
 
    protected Event(String name, String from, String to, EventCommand onSuccess){
        this.name = name;
        this.from1 = from;
        this.to = to;
        this.onSuccess = onSuccess;
    }
 
    protected Event(String name, String[] from, String to){
        this.name = name;
        this.from2 = from;
        this.to = to;
    }
 
    protected Event(String name, String[] from, String to, EventCommand onSuccess){
        this.name = name;
        this.from2 = from;
        this.to = to;
        this.onSuccess = onSuccess;
    }
}

Classe Transition:
Analogamente as classes State e Event, essa classe é representativa de uma transição. Possui como atributos o nome do evento que causa a transição, o(s) nome(s) do(s) estado(s) de partida e o nome do estado de chegada. Assim com a classe Event, o construtor a ser utilizado dependerá da existência d eum ou mais estados de saída para uma transição.

package stateMachine;
 
class Transition {
    String event;
    String fromState1;
    String[] fromState2;
    String toState;
 
    protected Transition(String event, String fromState, String toState){
        this.event = event;
        this.fromState1 = fromState;
        this.toState = toState;
    }
 
    protected Transition(String event, String[] fromState, String toState){
        this.event = event;
        this.fromState2 = fromState;
        this.toState = toState;
    }
}

Excessões:
As excessões são classes que herdam propriedades das classes Exception e RuntimeException. Não há nenhuma modificação nos seus constructors, todos se referem à superclasse.
Classe UndefinedTransitionException:
Subclasse de Exception. Responsável por lançar uma excessão para uma transição inválida (com estado de partida inexistente).

package stateMachine;
 
public class UndefinedTransitionException extends Exception {
    public UndefinedTransitionException() {
    super();
    }
 
    public UndefinedTransitionException(String message) {
    super(message);
    }
 
    public UndefinedTransitionException(String message, Throwable cause) {
        super(message, cause);
    }
 
    public UndefinedTransitionException(Throwable cause) {
        super(cause);
    }
}

Classe UndefinedStateException:
Subclasse de RuntimeException. Responsável por lançar uma excessão para um estado inválido (com estado de chegada inexistente na transição ).

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

Classe UndefinedEventException:
Subclasse de RuntimeException. Responsável por lançar uma excessão de um evento inválido (evento inexistente).

package stateMachine;
 
public class UndefinedEventException extends RuntimeException{
    public UndefinedEventException() {
    super();
    }
 
    public UndefinedEventException(String message) {
    super(message);
    }
 
    public UndefinedEventException(String message, Throwable cause) {
        super(message, cause);
    }
 
    public UndefinedEventException(Throwable cause) {
        super(cause);
    }
}

Classe StateMachine:
A classe foi implementada a partir do modelo de interface dado no relatório. Entre seus atributos ela possui quatro vetores, um para alocar todos os estados da máquina, um para os eventos, um para as transições e um último para os observers. Esses observer são registrados e notificados através dos métodos registerObserver e notifyObservers. Existem dois métodos para criação de estados, um deles recebe apenas o nome do estado e o outro recebe, além do novo, um comando StateCommand para ser executado na entrada do estado. Analogamente, nos métodos para criação de eventos, também existe um que recebe um comando EventCommand para ser executado na ação do evento. Os métodos para criação de eventos e transições podem receber como estado inicial um ou vários estados, essa diferenciação é feita nos construtores das classes State e Transition. A escolha do estado inicial é feita através do método setInitialState. O método setGuardToEvent recebe uma implementação da interface EventGuard e funciona impondo uma condição para a ocorrência de um certo evento.
O método getCurrentStateName simplesmente retorna o nome do estado atual em que a máquina se encontra. Finalmente o método fireEvent é o mais extenso de toda a classe. Ele é responsável pelo lançamento de um evento e é nele que são lançadas todas as três excessões de estado, evento e transição. Nesse método são checadas as condições para que um evento ocorra e em qual transição ele ocorrerá. Foi necessário fazer uma modificação no modelo do roteiro, pois nele esse último metodo lançava apenas uma excessão de transição e as excessões de estado e evento não eram lançadas em nenhum outro método.

package stateMachine;
 
import java.util.Vector;
 
public class StateMachine {
    private String state;
    private Vector<State> states = new Vector<State>();
    private State currentState;
    private Vector<Event> events = new Vector<Event>();
    private Vector<Transition> transitions = new Vector<Transition>();
    private Vector<Observer> observers = new Vector<Observer>();
    private EventGuard g;
    private Event guard;
 
    //comportamento de observável
    public void registerObserver(Observer observer)
    {
        this.observers.add(observer);
    }
    protected void notifyObservers()
    {
        for(Observer obs : this.observers) obs.update(this,currentState.name);
    }
 
    //métodos para a definição da máquina
    public void createState(String name, StateCommand onEnter)
    {
         State estado  = new State(name, onEnter);
         this.states.add(estado);
    }
    public void createState(String name)
    {
         State estado  = new State(name);
         this.states.add(estado);
    }
    public void setInitialState(String name)
    {
        for(State est : this.states){
            if(est.name == name) this.currentState = est;
        }
    }
    public void createEvent(String name, String[] from, String to, EventCommand onSuccess)
    {
        Event evento = new Event(name, from, to, onSuccess);
        this.events.add(evento);
        this.appendTransitionToEvent(name, from, to);
    }
    public void createEvent(String name, String[] from, String to)
    {
        Event evento = new Event(name, from, to);
        this.events.add(evento);
        this.appendTransitionToEvent(name, from, to);
    }
    public void createEvent(String name, String from, String to,  EventCommand onSuccess)
    {
        Event evento = new Event(name, from, to, onSuccess);
        this.events.add(evento);
        this.appendTransitionToEvent(name,from,to);
    }
    public void createEvent(String name, String from, String to)
    {
        Event evento = new Event(name, from, to);
        this.events.add(evento);
        this.appendTransitionToEvent(name,from,to);
    }
    public void appendTransitionToEvent(String event, String fromState, String toState)
    {
        Transition transition = new Transition(event,fromState,toState);
        this.transitions.add(transition);
    }
    public void appendTransitionToEvent(String event, String[] fromState, String toState)
    {
        Transition transition = new Transition(event,fromState,toState);
        this.transitions.add(transition);        
    }
    public void setGuardToEvent(String event,EventGuard g)
    {
        for(Event evn : events) if(evn.name == event) this.guard = evn;
        this.g = g;
    }
 
    // métodos para a execução da máquina
    public boolean fireEvent(String name) throws UndefinedTransitionException, UndefinedStateException, UndefinedEventException
    {
        boolean ok = false;
        if(g == null || g.shouldPerform() || guard.name != name){
            State stateFrom = this.currentState;
            Event event = null;
            Transition transition = null;
 
            for(Event evn : this.events) if(evn.name == name){
                ok = true;
            }
            if(!ok) throw new UndefinedEventException("Indique um evento existente.");
 
            if(ok){
                ok = false;
                for(Transition trn : this.transitions)
                    if(trn.event == name){
                        if(trn.fromState1 != null && trn.fromState1 == this.currentState.name){
                            transition = trn;
                            ok = true;
                        }
                        else if(trn.fromState2 != null){
                            for(String nst : trn.fromState2) if(nst == this.currentState.name){
                                transition = trn;
                                ok = true;
                            }
                        }
                    }
            }
            if(!ok) throw new UndefinedTransitionException("Transição não existente!");
            if(ok){
                ok = false;
                if(transition != null) for(State est : this.states){
                    if(est.name == transition.toState){
                        this.currentState = est;
                        ok = true;
                    }
                }
            }
            if(!ok) throw new UndefinedStateException("Indique um estado existente.");
            if(ok){
                if(event != null) if(event.onSuccess != null) event.onSuccess.execute(stateFrom.name);
                if(this.currentState.onEnter != null) this.currentState.onEnter.execute();
            }    
 
        }
        return ok;
    }
 
    public String getCurrentStateName()
    {
        return this.currentState.name;
    }
 
}

Diagrama de classes da biblioteca stateMachine
diagrama_de_classes_stateMachine

Aplicação dada

Num pacote aplicacao1, foram implementadas as classes WithSM, Professor, Turma e TurmaObserver como indicadas no roteiro.
Entretanto uma pequena modificação foi feita com relação ao tratamento de excessões. Além do tratamento da excessão UndefinedTransitionException mostrada no método fire da classe WithSM, também é necessário fazer o tratamento das excessões UndefinedStateException e UndefinedEventException. Além disso, da forma como o evento fire da classe Turma estava anteriormente, mesmo que ocorra uma excessão, o método notifyObservers ainda é chamado o que causa um resulatado errado da máquina. Dessa forma, decidiu-se por extender o lançamento das excessões até as classes Professor e Turma, onde elas finalmente são tratadas. As modificações das classes estão mostradas abaixo.


Classe WithSM:

public void fire(String eventName) throws UndefinedTransitionException, UndefinedEventException, UndefinedStateException{
          sm.fireEvent(eventName);
    }

Classe Turma:

public void fire(String event) {
        try {
            super.fire(event);
            notifyObservers();
        } catch(UndefinedTransitionException e1) {
            System.out.println("ERRO: trasição não definida: " + e1.getMessage());
        } catch(UndefinedEventException e2) {
            System.out.println("ERRO: evento não definido: " + e2.getMessage());
        } catch(UndefinedStateException e3) {
            System.out.println("ERRO: estado não definido: " + e3.getMessage());
        }
    }

Classe Professor:

public void updateFromTurma(Turma t) {
        try {
            if (t.getState() == "prestando atenção" || t.getState() == "cancerizando nos labs") {
                this.fire("motivar");
            } else {
                this.fire("desmotivar");
            }
        } catch(UndefinedTransitionException e1) {
            System.out.println("ERRO: trasição não definida: " + e1.getMessage());
        } catch(UndefinedEventException e2) {
            System.out.println("ERRO: evento não definido: " + e2.getMessage());
        } catch(UndefinedStateException e3) {
            System.out.println("ERRO: estado não definido: " + e3.getMessage());
        }
    }

Foi realizado o seguinte teste:

public void TesteAplicativoDado() {
        StateMachine sm = new StateMachine();
        Professor professor = new Professor();
        Turma turma = new Turma();
        turma.registerObserver(professor);
        //estágio inicial
        System.out.println("Turma " + turma.getState());
        professor.update(sm, professor.getState());
        System.out.println(professor.fazerProva());
        System.out.println(professor.prepararAula());
        //turma continua dormindo
        System.out.println("\nTurma continua dormindo");
        turma.fire("dormir");
        professor.update(sm, professor.getState());
        System.out.println(professor.fazerProva());
        System.out.println(professor.prepararAula());        
        //turma canceriza
        System.out.println("\nTurma canceriza");
        turma.fire("cancerizar");
        professor.update(sm, professor.getState());
        //turma volta a dormir
        System.out.println("\nTurma dorme");
        turma.fire("dormir");
        professor.update(sm, professor.getState());
        //turma usa notebook e professor fica irado
        System.out.println("\nTurma usa notebook");
        turma.fire("usar_note");
        professor.update(sm, professor.getState());
        System.out.println(professor.fazerProva());
        System.out.println(professor.prepararAula());
        //turma fica atenta
        System.out.println("\nTurma fica atenta");
        turma.fire("ser_atenta");
        professor.update(sm, professor.getState());
        System.out.println(professor.fazerProva());
        System.out.println(professor.prepararAula());      
        //turma continua atenta mas o professor continua vacinado
        System.out.println("\nTurma continua atenta");
        turma.fire("ser_atenta");
        professor.update(sm, professor.getState());
        //turma observa que o professor nunca mais vai ficar motivado e volta a dormir
        System.out.println("\nTurma dorme");
        turma.fire("dormir");
        professor.update(sm, professor.getState());
        //turma volta a ficar atenta mas o professor continua vacinado
        System.out.println("\nTurma atenta");
        turma.fire("ser_atenta");
        professor.update(sm, professor.getState());
        //professor ganha um super aumento
        professor.setGanhandoMilhoesDeDolares(true);
        System.out.println("\nProfessor ganha super aumento!");
        //como professor nunca mais ficará irado, turma volta a dormir
        System.out.println("\nTurma dorme");
        turma.fire("dormir");
        professor.update(sm, professor.getState());
 
    }

Nesse teste, foi obtida a seguinte saída:

Turma dormindo
Meu estado agora é motivado
Prova com noção
Aula preparada com carinho (5 horas)

Turma continua dormindo
Meu estado agora é desmotivado
Prova ruim, correção carteada
Aula preparada com pressa (1 hora)

Turma canceriza
Meu estado agora é motivado

Turma dorme
Meu estado agora é desmotivado

Turma usa notebook
nunca mais serei o mesmo!
Meu estado agora é irado
Prova sem noção, média D
Aula jogada (5 min)

Turma fica atenta
Meu estado agora é vacinado
Prova ruim, correção carteada
Aula preparada com pressa (1 hora)

Turma continua atenta
Meu estado agora é vacinado

Turma dorme
nunca mais serei o mesmo!
Meu estado agora é irado

Turma atenta
Meu estado agora é vacinado

Professor ganha super aumento!

Turma dorme
Meu estado agora é vacinado


Aplicação Feita

A seguinte aplicação foi desenvolvida tendo como base a biblioteca de máquina de estados implementada e os exemplos vistos.
aplicação_feita
Temos representadas duas máquinas de estados, a da esquerda Aluno e a da direita ITA.
A implementação das mesmas está mostrada abaixo.


Classe WithSM2:
Idêntica a WithSM

package aplicacao2;
 
import stateMachine.*;
public class WithSM2
{
    protected StateMachine sm;
    public WithSM2() {
         sm = new StateMachine();
    }
 
    public void fire(String eventName) throws UndefinedTransitionException, UndefinedEventException, UndefinedStateException{
          sm.fireEvent(eventName);     
    }
 
    public String getState(){
        return sm.getCurrentStateName();
    }
}

Interface ITAobserver:
Análoga a TurmaObserver.

package aplicacao2;
 
public interface ITAobserver
{
    void updateFromITA(ITA ita);
}

Classe ITA:
A classe ITA possui quatro estados, labSuga, labReagendado, provaChance, provaImpossivel. Os eventos correspondem diretamente a esses estados, se em qualquer estado o evento aplicar_prova_impossivel for utilizado, a máquina passará para o estado provaImpossivel. Quando algum evento é disparado ele notifica todos os ITAobserver registrados no mesmo.
package aplicacao2;
 
import java.util.logging.Level;
import java.util.logging.Logger;
import stateMachine.*;
import java.util.Vector;
 
public class ITA extends WithSM2
{
    private Vector<ITAobserver> observers = new Vector<ITAobserver>();
 
    public ITA() {
         super();
         sm.createState("labSuga");
         sm.createState("labReagendado");
         sm.createState("provaChance");
         sm.createState("provaImpossivel");
         sm.setInitialState("labSuga");
 
         String from[] = {"labSuga", "labReagendado", "provaChance", "provaImpossivel" };
         sm.createEvent("aplicar_lab_suga", from, "labSuga");
         sm.createEvent("reagendar_lab", from, "labReagendado");
         sm.createEvent("aplicar_prova_chance", from, "provaChance");
         sm.createEvent("aplicar_prova_impossivel", from, "provaImpossivel");
    }
 
    public void registerObserver(ITAobserver observer){
        observers.add(observer);
    }
 
    private void notifyObservers() {
        for(ITAobserver obs : observers) {
            obs.updateFromITA(this);
        }
    }
 
    public void fire(String event) {
        try {
            super.fire(event);
            notifyObservers();
        } catch(UndefinedTransitionException e1) {
            System.out.println("ERRO: trasição não definida: " + e1.getMessage());
        } catch(UndefinedEventException e2) {
            System.out.println("ERRO: evento não definido: " + e2.getMessage());
        } catch(UndefinedStateException e3) {
            System.out.println("ERRO: estado não definido: " + e3.getMessage());
        }
    }
}

Classe Aluno:
A classe Aluno possui os estados ele, sugado, tranquilo e mec, e os eventos sugar e cocar. Quando um aluno tranquilo se suga ele passa para sugado e assim por diante. Entretanto um aluno mec se suga demais com pouco e já passa diretamente de mec para sugado. Já um outro aluno que chegue no estado ele nunca mais consegue sair desse estado. O Alino tem dois métodos que retornam String, são eles fazerProva e assistirAula. A frase de retorno vai depender do estado do aluno. Na definição das condições iniciais no construtor pode-se observar a utilização de classes anônimas. Essas classes são construídas com base nas classes abstratas e interfaces da biblioteca da maquina de estados e o coro dos seus métodos são definidos na própria chamada em que eles são passados. Através do método setSafo o aluno pode virar safo e , pela condição do EventGuard, nunca mais se suga.
package aplicacao2;
 
import java.util.logging.Level;
import java.util.logging.Logger;
import stateMachine.UndefinedEventException;
import stateMachine.UndefinedStateException;
import stateMachine.UndefinedTransitionException;
 
public class AlunoITA extends WithSM2 implements stateMachine.Observer, ITAobserver
{
 
    public AlunoITA() {
        super();
        setUpSM();
        sm.registerObserver(this);
    }
 
    public String fazerProva() {
        if(getState() == "mec")
            return "Prova feita em 30 min (Nota R)";
        else if(getState() == "tranquilo")
            return "Prova feita no tempo médio (Nota B)";
        else if(getState() == "sugado")
            return "Prova feita de qualquer jeito (Nota I/D)";
        else
            return "Prova feita no tempo máximo, último aluno a sair (Nota L/D)";
    }
 
    public String assistirAula() {
        if(getState() == "tranquilo")
            return "Aluno prestando atenção na aula";
        else if(getState() == "sugado")
            return "Aluno dormindo na aula";
        else if(getState() == "ele")
            return "Aluno procurando erro do professor na aula";
        else
            return "Aluno MEC feliz, aula extraordinária!!!";
    }
 
    private boolean safo = false;
    public boolean isSafo(){
        return safo;
    }
    public void setSafo(boolean is){
        safo = is;
    }
 
    public void update(stateMachine.StateMachine sm, String newStateName) {
        System.out.println("Meu estado agora é " + newStateName);
    }
 
    public void updateFromITA(ITA ita) {
        try {
            if (ita.getState() == "labSuga" || ita.getState() == "provaImpossivel") {
                this.fire("sugar");
            } else {
                this.fire("cocar");
            }
        } catch(UndefinedTransitionException e1) {
            System.out.println("ERRO: trasição não definida: " + e1.getMessage());
        } catch(UndefinedEventException e2) {
            System.out.println("ERRO: evento não definido: " + e2.getMessage());
        } catch(UndefinedStateException e3) {
            System.out.println("ERRO: estado não definido: " + e3.getMessage());
        }
    }
 
    private void setUpSM() {
 
        //criando estados
        sm.createState("mec");
        sm.createState("tranquilo");
        sm.createState("sugado");
        sm.createState("ele", new stateMachine.StateCommand(sm) {
            public void execute() {
                System.out.println("nunca mais serei o mesmo!");
            }
        } );
        sm.setInitialState("tranquilo");
 
        //evento sugar
        sm.createEvent("sugar", "tranquilo", "sugado", new stateMachine.EventCommand(sm) {
            public void execute(String previousState) {
                System.out.println("Sugou!");
            }
        });
        sm.appendTransitionToEvent("sugar", "mec", "sugado");
        String[] from = {"sugado", "ele"};
        sm.appendTransitionToEvent("sugar", from, "ele");
        sm.setGuardToEvent("sugar", new stateMachine.EventGuard() {
            public boolean shouldPerform(){
               return !isSafo();  //nunca fica sugado se é safo
            }
        });
 
        //evento cocar
        sm.createEvent("cocar", "sugado", "tranquilo", new stateMachine.EventCommand(sm) {
            public void execute(String previousState) {
                System.out.println("Coçou!");
            }
        });
        String[] from2 = {"tranquilo", "mec"};
        sm.appendTransitionToEvent("cocar", from2, "mec"); 
        sm.appendTransitionToEvent("cocar", "ele", "ele");
    }
 
}

Diagrama de classes da aplicação feita:
diagrama_de_classes_aplicação


Teste realizado:

public void TestarAplicacaoFeita() {
        StateMachine sm = new StateMachine();
        AlunoITA aluno1 = new AlunoITA();
        AlunoITA aluno2 = new AlunoITA();
        ITA ita = new ITA();
        ita.registerObserver(aluno1);
        ita.registerObserver(aluno2);
        aluno2.setSafo(true);
        System.out.println("Aluno2 é safo!");
        //estágio inicial
        System.out.println("Aluno1: ");
        aluno1.update(sm, aluno1.getState());
        System.out.println("Aluno2: ");
        aluno2.update(sm, aluno2.getState());
        System.out.println("Aluno1:\n" + aluno1.assistirAula());
        System.out.println(aluno1.fazerProva());
        System.out.println("Aluno2:\n" + aluno2.assistirAula());
        System.out.println(aluno2.fazerProva());
        //ITA reagenda lab
        System.out.println("\nITA tem lab 'reagendado'");
        ita.fire("reagendar_lab");
        System.out.println("Aluno1: ");
        aluno1.update(sm, aluno1.getState());
        System.out.println("Aluno2: ");
        aluno2.update(sm, aluno2.getState());
        System.out.println("Aluno1:\n" + aluno1.assistirAula());
        System.out.println(aluno1.fazerProva());
        //ITA aplica lab suga
        System.out.println("\nITA aplica lab suga");
        ita.fire("aplicar_lab_suga");
        System.out.println("Aluno1: ");
        aluno1.update(sm, aluno1.getState());
        System.out.println("Aluno2: ");
        aluno2.update(sm, aluno2.getState());
        System.out.println("Como a aluno2 não se suga, ele nunca mais deixara de ser MEC");
        //ITA aplica prova impossível
        System.out.println("\nITA aplica prova impossível");
        ita.fire("aplicar_prova_impossivel");
        System.out.println("Aluno1: ");
        aluno1.update(sm, aluno1.getState());
        System.out.println("Aluno1:\n" + aluno1.assistirAula());
        System.out.println(aluno1.fazerProva());
    }

Saída obtida:

Aluno2 é safo!
Aluno1:
Meu estado agora é tranquilo
Aluno2:
Meu estado agora é tranquilo
Aluno1:
Aluno prestando atenção na aula
Prova feita no tempo médio (Nota B)
Aluno2:
Aluno prestando atenção na aula
Prova feita no tempo médio (Nota B)

ITA tem lab 'reagendado'
Aluno1:
Meu estado agora é mec
Aluno2:
Meu estado agora é mec
Aluno1:
Aluno MEC feliz, aula extraordinária!!!
Prova feita em 30 min (Nota R)

ITA aplica lab suga
Aluno1:
Meu estado agora é sugado
Aluno2:
Meu estado agora é mec
Como a aluno2 não se suga, ele nunca mais deixara de ser MEC

ITA aplica prova impossível
nunca mais serei o mesmo!
Aluno1:
Meu estado agora é ele
Aluno1:
Aluno procurando erro do professor na aula
Prova feita no tempo máximo, último aluno a sair (Nota L/D)


Teste passado

No roteiro do lab foi passado o seguinte este para checagem da biblioteca.

public void testStateMachineSimple()
    {
        StateMachine sm = new StateMachine();
        sm.createState("feliz");
        sm.createState("triste");
        sm.setInitialState("triste");
        assertEquals("triste", sm.getCurrentStateName());
        sm.createEvent("jogar_bola", "triste", "feliz");
 
        try {
            sm.fireEvent("jogar_bola");
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
 
        assertEquals("feliz", sm.getCurrentStateName());
 
        try {
            sm.fireEvent("jogar_bola");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }
    }
 
    public void testArrayInFrom()
    {
        StateMachine sm = new StateMachine();
        sm.createState("feliz");
        sm.createState("triste");
        sm.createState("deprimido");
        sm.setInitialState("feliz");
        assertEquals("feliz", sm.getCurrentStateName());
        String[] from = {"triste", "feliz"};
        sm.createEvent("perder_mulher", from, "deprimido");
        try {
            sm.fireEvent("perder_mulher");
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
        assertEquals("deprimido", sm.getCurrentStateName());
        try {
            sm.fireEvent("perder_mulher");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }
 
        sm = new StateMachine(); //outra maquina
        sm.createState("feliz");
        sm.createState("triste");
        sm.createState("deprimido");
        sm.setInitialState("triste");
        assertEquals("triste", sm.getCurrentStateName());
        String[] from2 = {"triste", "feliz"};
        sm.createEvent("perder_mulher", from2, "deprimido");
        try {
            sm.fireEvent("perder_mulher");
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
        assertEquals("deprimido", sm.getCurrentStateName());
        try {
            sm.fireEvent("perder_mulher");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }
    }
 
    public void testMultipleTransitions() {
        StateMachine sm = new StateMachine();
        sm.createState("super_feliz");
        sm.createState("feliz");
        sm.createState("triste");
        sm.createState("deprimido");
        sm.setInitialState("super_feliz");
 
        String[] from = {"triste", "feliz"};
        sm.createEvent("perder_mulher", from, "deprimido");
 
        sm.appendTransitionToEvent("perder_mulher", "super_feliz", "triste");
 
        try {
            sm.fireEvent("perder_mulher");
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
        assertEquals("triste", sm.getCurrentStateName());
 
        try {
            sm.fireEvent("perder_mulher");
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
        assertEquals("deprimido", sm.getCurrentStateName());
 
        try {
            sm.fireEvent("perder_mulher");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }
    }
 
    public void testStateCommand() {
        StateMachine sm = new StateMachine();
        sm.createState("super_feliz");
        sm.createState("feliz");
        sm.createState("triste");
        sm.createState("deprimido");
        sm.setInitialState("super_feliz");
 
        sm.createEvent("perder_mulher", "super_feliz", "triste", new EventCommand(sm) {
            public void execute(String previousState) {
                System.out.println("Eu estou chorando!!! Não estou mais " + previousState +
                    ", agora estou " + this.machine.getCurrentStateName());
            }
        } );
 
        try {
            sm.fireEvent("perder_mulher");
        }catch (UndefinedTransitionException e) {
        }
 
    }
 
    public void testEventCommand() {
        StateMachine sm = new StateMachine();
        boolean called = false;
        StateCommand avisar = new StateCommand(sm) {
            public void execute() {
                System.out.println("Agora eu fiquei " + this.machine.getCurrentStateName());
            }
        };
        sm.createState("super_feliz");
        sm.createState("feliz", avisar);
        sm.createState("triste", avisar);
        sm.createState("deprimido", avisar);
        sm.setInitialState("super_feliz");
        sm.createEvent("perder_mulher", "super_feliz", "triste");
        try {
            sm.fireEvent("perder_mulher");
        }catch (UndefinedTransitionException e) {
        }
    }
 
     public void testGuard() {
        temSuperPoderes = true;
        StateMachine sm = new StateMachine();
        sm.createState("super_feliz");
        sm.createState("feliz");
        sm.createState("triste");
        sm.createState("deprimido");
        sm.setInitialState("super_feliz");
        sm.createEvent("perder_mulher", "super_feliz", "triste");
        sm.setGuardToEvent("perder_mulher", new EventGuard() {
            public boolean shouldPerform() {
                return !temSuperPoderes;
            }
        });
        try {
            assertFalse(sm.fireEvent("perder_mulher"));
        }catch (UndefinedTransitionException e) {
        }
        temSuperPoderes = false;
        try {
            assertTrue(sm.fireEvent("perder_mulher"));
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
    }
 
    private boolean temSuperPoderes = false;
 
    public boolean getTemSuperPoderes(){
        return temSuperPoderes;
    }
 
    public void testShouldReturnErrorIfEventDoesntExist() {
        StateMachine sm = new StateMachine();
        sm.createState("feliz");
        sm.createState("triste");
        sm.setInitialState("triste");
        assertEquals("triste", sm.getCurrentStateName());
        sm.createEvent("jogar_bola", "triste", "estado_nao_existe");
 
        try {
            sm.fireEvent("evento_nao_existente");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }catch (UndefinedEventException e) {
        }    
 
    }
 
    public void testShouldReturnErrorIfStateDoesntExist() {
        StateMachine sm = new StateMachine();
        sm.createState("feliz");
        sm.createState("triste");
        sm.setInitialState("triste");
        assertEquals("triste", sm.getCurrentStateName());
        sm.createEvent("jogar_bola", "triste", "estado_nao_existe");
 
        try {
            sm.fireEvent("jogar_bola");
            fail("deve gerar excessão");
        }catch(UndefinedStateException e) {
        }catch(UndefinedTransitionException e) {
        } 
    }

Todos os testes obtiveram resultado positivo:
testes.JPG

Conclusão

Nesse laboratório finalmente pode-se utilizar na prática os padrões de projeto vistos em sala. Algumas modificações precisaram ser feitas em relação a alguns códigos dados no roteiro como finalizados, entretanto essas modificações foram necessárias para a implementação correta da máquina de estados. Como a prática foi muito longa, os códigos desenvolvidos não ficaram resumidos, entretanto o objetivo principal da prática, que é a assimilação dos novos conceitos pode ser atingido.

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