Lab3 Francisco Germano

aluno: Francisco Germano
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 10/10/2008 (10)

Introdução

O laboratório consistiu na criação num pacote em java que fosse capaz de representar as ações de uma máquina de estados determinística. A máquina de estados genérica podería efetuar comandos ao chegar no estado e ao ter um evento disparado. Pode acontecer também que a condição de guarda seja disparada e um deteminado evento não ative a transição de um estado para outro. Para se encerrar o lab ocorreram testes com um exemplo dado em sala, com uma máquina criada pelo aluno e com uma classe de testes fornecida.

Desenvolvimento

No pacote statemachine foram desenvolvidas 10 classes. Elas são as seguintes

Event


Classe que representa os eventos na máquina de estado. Cada evento possui um nome que o identifica unicamente, um HashMap que armazena as transicoes associadas a esse evento (a transicao nada mais é do que uma relação associada a esse evento entre um estado inicial e um estado final), uma variável para guardar os comandos do evento caso ele possua e outra variável que guarda a guarda do evento se ela existir.

class Event {
 
    String name;
    HashMap mapTransition;
    EventCommand command;
    EventGuard guard;
 
    Event(String name) {
        this.name = name;
        mapTransition = new HashMap<State, State>();
        this.guard = null;
    }
}

EventCommand


Classe abstrata que é utilizada na forma de classe anônima nesse laboratório. Sua função é armazenar o comando que deve ser executado quanto um determinado evento for acionado.

public abstract class EventCommand {
 
    protected StateMachine machine;
 
    public EventCommand(StateMachine machine) {
        this.machine = machine;
    }
 
    public abstract void execute(String previousState);
}

EventGuard


Interface que também é implementada através de classes anônimas. Ela tem a função de implementar uma condição de guarda para o evento. Caso a condição de guarda seja verdadeira, não é possível alterar o estado da máquina com um determinado evento.

public interface EventGuard {
    boolean shouldPerform();
}

Observer


Interface que faz parte da implementação do padrão observer.

public interface Observer {
    void update(StateMachine sm, String newStateName);
}

State


Classe que representa os estados da máquina de estados. Cada estado possui um nome que o identifica unicamente e um comando que será executdo quando a máquina entrar no estado, caso ele seja definido.

class State {
 
    public String name;
    public StateCommand command;
 
    State(String name, StateCommand command) {
        this.name = name;
        this.command = command;
    }
}

StateCommand


Classe abstrata que foi utilizada no laboratório através de classes anônimas. Essa classe armazena o comando que deve ser executado quando a máquina de estados chega em um novo estado, esse comando nem sempre existirá.

public abstract class StateCommand {
 
    protected StateMachine machine;
 
    public abstract void execute();
 
    public StateCommand(StateMachine machine) {
        this.machine = machine;
    }
}

UndefinedEventException


Essa é uma extenção da classe RunTime exception, portanto ela pode lançar uma unchecked exception. Ela é utilizada quando o evento pelo qual o método chamou não existe.

public class UndefinedEventException extends RuntimeException{
 
}
}

UndefinedStateException


Essa é uma extenção da classe RuntimeException, portanto ela pode lançar uma unchecked exception. Ela é utilizada quando o estado pelo qual o método chamou não existe.

public class UndefinedStateException extends RuntimeException {
 
}

UndefinedTransitionException


Essa é uma extenção da classe Exception, portanto ela pode lançar uma exception. Ela é utilizada quando uma transição não definida é chamada. Isso pode ser verificado se for chamada um evento num estado no qual esse evento não gera efeito (transição inexistente).

public class UndefinedTransitionException extends Exception {
 
}

Para o teste do exemplo dado em sala (representando o Professor e a turma) foram criadas as seguintes 5 classes:

Professor


Classe que representa o professor. Ela é subclasse da Classe WithSM (possuindo acesso aos métodos de uma máquina de estado) e é observadora da classe Turma. Vale a pena destacar que caso o professor esteja com um salário de milhões o GuardEvent é ativado e ele não pode mais ser desmotivado.Os eventos que afetam o professor e os estados pelos quais ele pode passar estão representados no diagrama de estados abaixo:

estados-professor.jpg
public class Professor extends WithSM implements statemachine.Observer, TurmaObserver {
 
public Professor() {
        super();
        setUpSM();
        sm.registerObserver(this);
    }
 
    public String fazerProva() {
        if (getState() == "motivado") {
            return "Prova com noção";
        } else if (getState() == "desmotivado" || getState() == "vacinado") {
            return "Prova ruim, correção carteada";
        } else if (getState() == "irado") {
            return "Prova sem noção, média D";
        } else {
            return "Prova que nunca será feita";
        }
    }
 
    public String prepararAula() {
        if (getState() == "motivado") {
            return "Aula preparada com carinho (5 horas)";
        } else if (getState() == "desmotivado" || getState() == "vacinado") {
            return "Aula preparada com pressa (1 hora)";
        } else if (getState() == "irado") {
            return "Aula jogada (5 min)";
        } else {
            return "Aula que nunca será feita";
        }
    }
    private boolean ganhandoMilhoes = false;
 
    public boolean isGanhandoMilhoesDeDolares() {
        return ganhandoMilhoes;
    }
 
    public void setGanhandoMilhoesDeDolares(boolean is) {
        ganhandoMilhoes = is;
    }
 
    public void update(statemachine.StateMachine sm, String newStateName) {
        System.out.println("Meu estado agora é " + newStateName);
    }
 
    public void updateFromTurma(Turma t) {
        if (t.getState() == "prestando atenção" || t.getState() == "cancerizando nos labs") {
            this.fire("motivar");
        } else {
            this.fire("desmotivar");
        }
 
        String s= new String();
 
    }
 
    private void setUpSM() {
 
        //criando estados
        sm.createState("motivado");
        sm.createState("desmotivado");
        sm.createState("vacinado");
        sm.createState("irado", new statemachine.StateCommand(sm) {
 
            public void execute() {
                System.out.println("nunca mais serei o mesmo!");
            }
        });
        sm.setInitialState("motivado");
 
        //evento desmotivar
        sm.createEvent("desmotivar", "motivado", "desmotivado", new statemachine.EventCommand(sm) {
 
            public void execute(String previousState) {
                System.out.println("Fiquei mais desmotivado!");
            }
        });
        String[] from = {"desmotivado", "irado", "vacinado"};
        sm.appendTransitionToEvent("desmotivar", from, "irado");
        sm.setGuardToEvent("desmotivar", new statemachine.EventGuard() {
 
            public boolean shouldPerform() {
                return !isGanhandoMilhoesDeDolares();  //nunca fica desmotivado se ganha milhoes
 
            }
        });
 
        //evento motivar
        sm.createEvent("motivar", "desmotivado", "motivado", new statemachine.EventCommand(sm) {
 
            public void execute(String previousState) {
                System.out.println("Oba, fiquei mais motivado!");
            }
        });
        String[] from2 = {"irado", "vacinado"};
        sm.appendTransitionToEvent("motivar", from2, "vacinado");
        sm.appendTransitionToEvent("motivar", "motivado", "motivado");
 
    }
}

Turma


A classe Turma representa a turma de alunos que será observada pelo professor e é sub-classe de WithSM, implementando as funcionalidades da biblioteca de máquina de estado. A classe possui um método fire que além de executar a transição de estado avisa ao professor dessa transição (através do método notifyObservers). Os eventos estar_atenta e cancerizar motivam o professor, enquanto os eventos dormir e usar_note desmotivam o professor. O diagrama de estados abaixo representa todos os estados e as transições dessa máquina.

estados-turma.jpg
public class Turma extends WithSM {
 
    private Vector<TurmaObserver> observers = new Vector<TurmaObserver>();
 
    public Turma() {
        super();
        sm.createState("prestando atenção");
        sm.createState("cancerizando nos labs");
        sm.createState("dormindo");
        sm.createState("usando notebook");
        sm.setInitialState("dormindo");
 
        String from[] = {"prestando atenção", "cancerizando nos labs", "dormindo", "usando notebook"};
        sm.createEvent("dormir", from, "dormindo");
        sm.createEvent("cancerizar", from, "cancerizando nos labs");
        sm.createEvent("usar_note", from, "usando notebook");
        sm.createEvent("ser_atenta", from, "prestando atenção");
    }
 
    public void registerObserver(TurmaObserver observer) {
        observers.add(observer);
    }
 
    private void notifyObservers() {
        for (TurmaObserver obs : observers) {
            obs.updateFromTurma(this);
        }
    }
 
    @Override
    public void fire(String event) {
        super.fire(event);
        notifyObservers();
    }
}

TurmaObserver


Interface que serve para implementar o padrão observer entre as classes Turma e Professor.

public interface TurmaObserver {
 
    void updateFromTurma(Turma t);
}

WithSM


Classe que cria uma Maquina de estado utilizando a classe StateMachine. No método fire ele faz a transicao dos eventos e trata as exceção caso seja chamado uma transição que não existe.

public class WithSM {
protected StateMachine sm;
    public WithSM() {
         sm = new StateMachine();
    }
 
    public void fire(String eventName){
        try{
          sm.fireEvent(eventName);
        } catch(UndefinedTransitionException e) {
            System.out.println("ERRO: trasição não definida: " + e.getMessage());
        }
    }
 
    public String getState(){
        return sm.getCurrentStateName();
    }
 
}

MainExemplos


A classe MainExemplos foi criada para implementarmos uma interface simples para testarmos o exemplo do professor-turma diretamente na linha de comando.

public class MainExemplos {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        int m = 1, p, t, s;
        Scanner scan = new Scanner(System.in);
        Professor prof = new Professor();
        Turma comp = new Turma();
        comp.registerObserver(prof);
        do {
            System.out.println("1 - Acoes para o professor");
            System.out.println("2 - Acoes para a turma");
            System.out.println("0 - Sair");
            do {
                m = scan.nextInt();
            } while (m != 0 && m != 1 && m != 2);
            switch (m) {
                case 1: {
                    System.out.println("\n1 - Preparar aula");
                    System.out.println("2 - Fazer prova");
                    System.out.println("3 - Ajustar salario");
                    do {
                        p = scan.nextInt();
                    } while (p != 1 && p != 2 && p != 3);
                    if (p == 1) {
                        System.out.println(prof.prepararAula()+"\n");
                    }
                    if (p == 2) {
                        System.out.println(prof.fazerProva()+"\n");
                    }
                    if (p == 3) {
                        System.out.println("\n1 - Pagar salario normal");
                        System.out.println("2 - Pagar salario milionario");
                        do {
                            s = scan.nextInt();
                        } while (s != 1 && s != 2);
                        if (s == 1) {
                            prof.setGanhandoMilhoesDeDolares(false);
                        }
                        if (s == 2) {
                            prof.setGanhandoMilhoesDeDolares(true);
                        }
                    }
                    break;
                }
                case 2: {
                    System.out.println("\n1 - Prestar atencao na aula");
                    System.out.println("2 - Cancerizar no lab");
                    System.out.println("3 - Usar notebook");
                    System.out.println("4 - Dormir");
                    do {
                        t = scan.nextInt();
                    } while (t != 1 && t != 2 && t != 3 && t != 4);
                    if (t == 1) {
                        comp.fire("ser_atenta");
                    }
                    if (t == 2) {
                        comp.fire("cancerizar");
                    }
                    if (t == 3) {
                        comp.fire("usar_note");
                    }
                    if (t == 4) {
                        comp.fire("dormir");
                    }
                    break;
                }
            }
        } while (m != 0);
    }
}

Vários testes foram realizados e todos foram bem sucedidos. Abaixo seguem algumas telas de testes manuais.

Professor-TurmaTeste01.jpg
Professor-TurmaTeste02.jpg
Professor-TurmaTeste03.jpg
Professor-TurmaTeste04.jpg
Professor-TurmaTeste05.jpg
Professor-TurmaTeste06.jpg

Lançando foguete


Na sequencia foi solicitado a criacao de uma maquina de estados sugerida pelo aluno para testar o programa. A máquina de estados proposta foi baseada na seguinte contextualização:

Para a FAB a criação de foguetes controlados a distância para serem utilizados como armas é uma nova prioridade. Um jovem engenheiro da computação formado no ITA, que se preocuoa com o desenvolvimento tecnológico da nação, foi trabalhar nesse projeto e contribui criando um novo software para o motor do foguete e para controlá-lo remotamente.

Ele partiu da simples idéia de uma máquina de estado na qual os estágios do motor seriam acionados progressivamente no lançamento e desligados na ordem reversa quando o foguete atingir a altitude desejada.

Um processo de segurança também foi criado pelo engenheiro, para evitar acidentes. A qualquer momento que um erro for detectado, o trabalho dos motores do foguete é interrompido para que explosões sejam previnidas.

As ações do foguete que podem ser mudar de direção, danificar o tanque de combustível (caso controle do foguete seja obtido por uma força inimíga), utilizar nitro e inicializar um processo controlado de autodestruição (caso algum problema no motor seja identificado ou se o foguete for cair em um local errado, por exemplo).

Os diagramas de estados abaixo detalham as idéias do iteano (diagrama do motor e do foguete respectivamente).

MotorFoguete2.jpg
MotorFoguete2.jpg

Para que a máquina de estados funciona-se e fosse testada foram criadas 5 classes (Foguete, MotorFoguete, MotorObserver, WithSM e MainFoguete). Segue a descricao de cada uma das classes (detalhe para a utilização do padrão observer).

Foguete


Classe que representa o foguete.

public class Foguete extends WithSM implements statemachine.Observer, MotorObserver {
 
    float velocidade;//A velocidade sera medida em numeros de Mach
    int dirigibilidade;//Capacidade do foguete de fazer curvas rapidamente
    boolean fuel;//Booleano que se mantem verdadeiro enquanto o foguete possuir combustivel
 
    public Foguete() {
        super();
        setUpSM();
        sm.registerObserver(this);
        velocidade = 0;
        dirigibilidade = 0;
        fuel = true;
    }
 
    //@SuppressWarnings("empty-statement")
    private void setUpSM() {
        sm.createState("modoPlanar", new statemachine.StateCommand(sm) {
 
            public void execute() {
 
                System.out.println("O motor esta desligado e o foguete mantem a velocidade minima para voar");
            }
        });
 
        sm.createState("modoCruzeiro", new statemachine.StateCommand(sm) {
 
            public void execute() {
                System.out.println("O foguete esta viajando na sua velocidade ideal.");
            }
        });
 
        sm.createState("autoDestruicao", new statemachine.StateCommand(sm) {
 
            public void execute() {
                int i;
                char c;
                System.out.println("O foguete explodira em:");
                for (i = 5; i >= 0; i--) {
                    try {
                        System.out.println(i);
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Foguete.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                System.out.println("BOOOOOMMM!!!!");
            }
        });
 
        sm.createState("modoRapido", new statemachine.StateCommand(sm) {
 
            public void execute() {
                System.out.println("O foguete operando em modo acelerado.");
            }
        });
 
        sm.createState("modoTurbo", new statemachine.StateCommand(sm) {
 
            public void execute() {
                System.out.println("O foguete esta no limite suportavel. CUIDADO!!!");
            }
        });
 
        sm.createState("parado");
 
        sm.setInitialState("parado");
 
        sm.createEvent("Acelerar", "parado", "modoCruzeiro", new statemachine.EventCommand(sm) {
 
            @Override
            public void execute(String previousState) {
                System.out.println("ACELERA!!");
                previousState = sm.getCurrentStateName();
                if (previousState.equals("parado")) {
                    System.out.println("Dada a partida no foguete.");
                }
                if (previousState.equals("modoTurbo")) {
                    System.out.println("Houston, we have a problem!");
                }
            }
        });
 
        sm.appendTransitionToEvent("Acelerar", "modoPlanar", "modoCruzeiro");
 
        sm.appendTransitionToEvent("Acelerar", "modoCruzeiro", "modoRapido");
 
        sm.appendTransitionToEvent("Acelerar", "modoRapido", "modoTurbo");
 
        sm.appendTransitionToEvent("Acelerar", "modoTurbo", "autoDestruicao");
 
        sm.createEvent("Desacelerar", "modoTurbo", "modoRapido", new statemachine.EventCommand(sm) {
 
            @Override
            public void execute(String previousState) {
                System.out.println("Desacelerando!");
            }
        });
 
        sm.appendTransitionToEvent("Desacelerar", "modoRapido", "modoCruzeiro");
 
        sm.appendTransitionToEvent("Desacelerar", "modoCruzeiro", "modoPlanar");
 
        sm.appendTransitionToEvent("Desacelerar", "modoPlanar", "modoPlanar");
 
        String from[] = {"modoPlanar", "modoCruzeiro", "modoRapido", "modoTurbo"};
 
        sm.createEvent("Desintegrar", from, "autoDestruicao");
 
        sm.setGuardToEvent("Acelerar", new statemachine.EventGuard() {
 
            public boolean shouldPerform() {
                return isThereFuel();  //nao e possivel acelerar caso nao exista combustivel no tanque
 
            }
        });
 
    }
 
    public void update(StateMachine sm, String newStateName) {
        System.out.println("Meu estado agora é " + newStateName);
    }
 
    public void updateFromMotor(MotorFoguete mf, String mens) {
        if (mens.equals("Abortar")) {
            this.fire("Desintegrar");
        } else {
            String str = mf.getState();
            if (str.equals("desligado")) {
                this.fire("Desacelerar");
                this.velocidade = 1;
                this.dirigibilidade = 3;
            } else if (str.equals("estagio1")) {
                if (mens.equals("Ligar")) {
                    this.fire("Acelerar");
                    if (this.fuel == true) {
                        this.velocidade = 5;
                        this.dirigibilidade = 2;
                    }
                }
                if (mens.equals("Desligar")) {
                    this.fire("Desacelerar");
                    this.velocidade = 5;
                    this.dirigibilidade = 2;
                }
            } else if (str.equals("estagio2")) {
                if (mens.equals("Ligar")) {
                    this.fire("Acelerar");
                    if (this.fuel == true) {
                        this.velocidade = 10;
                        this.dirigibilidade = 1;
                    }
                }
                if (mens.equals("Desligar")) {
                    this.fire("Desacelerar");
                    this.velocidade = 10;
                    this.dirigibilidade = 1;
                }
            } else if (str.equals("estagio3")) {
                this.fire("Acelerar");
                if (this.fuel == true) {
                    this.velocidade = 30;
                    this.dirigibilidade = 0;
                }
            }
            this.statusFoguete();
        }
    }
 
    public void statusFoguete() {
        System.out.println("\nO foguete esta no modo " + this.getState() + " e o seu status e:");
        System.out.println("Velocidade de " + this.velocidade + "mach.");
        System.out.println("Dirigibilidade nivel " + this.dirigibilidade + "\n");
    }
 
    public void virar45(String direcao) {
        if (dirigibilidade >= 1) {
            System.out.println("Curva de 45 graus para " + direcao);
        } else {
            System.out.println("Nao e possivel fazer a curva com a atual velocidade do foguete");
            this.statusFoguete();
        }
    }
 
    public void virar90(String direcao) {
        if (dirigibilidade >= 2) {
            System.out.println("Curva de 90 graus para " + direcao);
        } else {
            System.out.println("Nao e possivel fazer a curva com a atual velocidade do foguete");
            this.statusFoguete();
        }
    }
 
    public void virar180() {
        if (dirigibilidade >= 3) {
            System.out.println("Giro de 180 graus.");
        } else {
            System.out.println("Nao e possivel fazer a curva com a atual velocidade do foguete");
            this.statusFoguete();
        }
    }
 
    public void nitro() {
        int i,  j;
        System.out.println("Nitro faz com que o foguete tenha a velocidade aumentada sem que um novo estagio do motor seja acionado");
        System.out.println("Tem certeza que quer utilizar nitro?\n1-Sim\n2-Nao");
        Scanner scan = new Scanner(System.in);
        do {
            i = scan.nextInt();
        } while (i != 1 && i != 2);
        if (i == 1) {
            System.out.println("OOOOWWWW! Nitro is activated for 10 seconds!");
            this.fire("Acelerar");
            if (!this.getState().equals("autoDestruicao")) {
                for (j = 0; j < 10; j++) {
                    try {
                        System.out.println(j);
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Foguete.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                this.fire("Desacelerar");
                if (this.getState().equals("modoPlanar")) {
                    this.velocidade = 1;
                    this.dirigibilidade = 3;
                }
            }
        }
    }
 
    public void setFuel(boolean is) {
        this.fuel = is;
    }
 
    public boolean isThereFuel() {
        return this.fuel;
    }
}

WithSM


Classe identica a utilizada no exemplo anterior. Ela é super classe de Foguete e de MotorFoguete.

public class WithSM {
protected StateMachine sm;
    public WithSM() {
         sm = new StateMachine();
    }
 
    public void fire(String eventName){
        try{
          sm.fireEvent(eventName);
        } catch(UndefinedTransitionException e) {
            System.out.println("ERRO: trasição não definida: " + e.getMessage());
        }
    }
 
    public String getState(){
        return sm.getCurrentStateName();
    }
 
}

MotorFoguete


Classe que representa o motor do foguete. Ela será observada pelo foguete.

public class MotorFoguete extends WithSM {
 
    private Vector<MotorObserver> observers = new Vector<MotorObserver>();
    boolean fuel;
 
    public MotorFoguete() {
        super();
        fuel=true;
 
        sm.createState("desligado");
        sm.createState("estagio1");
        sm.createState("estagio2");
        sm.createState("estagio3");
        sm.setInitialState("desligado");
 
        String from[]= {"desligado","estagio1", "estagio2", "estagio3"};
 
        sm.createEvent("Abortar", from, "desligado");
 
        sm.createEvent("Ligar", "desligado", "estagio1");
        sm.appendTransitionToEvent("Ligar", "estagio1", "estagio2");
        sm.appendTransitionToEvent("Ligar", "estagio2", "estagio3");
        sm.appendTransitionToEvent("Ligar", "estagio3", "estagio3");
 
        sm.setGuardToEvent("Ligar", new statemachine.EventGuard() {
 
            public boolean shouldPerform() {
                return isThereFuel(); //nao e possivel ligar o motor caso nao exista combustivel no tanque
            }
        });
 
        sm.createEvent("Desligar", "estagio1", "desligado");
        sm.appendTransitionToEvent("Desligar", "estagio2", "estagio1");
        sm.appendTransitionToEvent("Desligar", "estagio3", "estagio2");
        sm.appendTransitionToEvent("Desligar", "desligado", "desligado");
    }
 
    public void setFuel(boolean is){
        fuel=is;
    }
 
    public boolean isThereFuel() {
        return this.fuel;
    }
 
    public void registerObserver(MotorObserver observer) {
        observers.add(observer);
    }
 
    private void notifyObservers(String mens) {
        for (MotorObserver obs : observers) {
            obs.updateFromMotor(this,mens);
        }
    }
 
    @Override
    public void fire(String event) {
        super.fire(event);
        notifyObservers(event);
    }
}

MotorObserver


Interface para implementar o método observer entre o motor e o foguete.

public interface MotorObserver {
 
    void updateFromMotor(MotorFoguete mf, String mens);
}

MainFoguete


Classe Main criada para testes manuais das classes via linha de comando

public class MainFoguete {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        int s, f, mf, c, i;
        Scanner scan = new Scanner(System.in);
        Foguete fog = new Foguete();
        MotorFoguete mfog = new MotorFoguete();
        mfog.registerObserver(fog);
        do {
            System.out.println("1 - Ordens para o foguete");
            System.out.println("2 - Ordens para o motor do foguete");
            System.out.println("0 - Sair");
            do {
                s = scan.nextInt();
            } while (s != 0 && s != 1 && s != 2);
            switch (s) {
                case 1: {
                    System.out.println("\n1 - Virar");
                    System.out.println("2 - Usar nitro");
                    System.out.println("3 - Alterar tanque de combustivel");
                    System.out.println("4 - Iniciar sistema de autodestruicao do foguete");
                    System.out.println("5 - Verificar status do foguete");
                    do {
                        f = scan.nextInt();
                    } while (f != 1 && f != 2 && f != 3 && f != 4 && f != 5);
                    if (f == 1) {
                        System.out.println("1 - Tentar virar 45 graus.");
                        System.out.println("2 - Tentar virar 90 graus.");
                        System.out.println("3 - Tentar virar 180 graus.");
                        do {
                            c = scan.nextInt();
                        } while (c != 1 && c != 2 && c != 3);
                        if (c == 1 || c == 2) {
                            System.out.println("1 - Virar para direita");
                            System.out.println("2 - Virar para esquerda");
                            do {
                                i = scan.nextInt();
                            } while (i != 1 && i != 2);
                            if (c == 1) {
                                if (i == 1) {
                                    fog.virar45("direita");
                                }
                                if (i == 2) {
                                    fog.virar45("esquerda");
                                }
                            }
                            if (c == 2) {
                                if (i == 1) {
                                    fog.virar90("direita");
                                }
                                if (i == 2) {
                                    fog.virar90("esquerda");
                                }
                            }
                        }
                        if (c == 3) {
                            fog.virar180();
                        }
                    }
                    if (f == 2) {
                        fog.nitro();
                    }
                    if (f == 3) {
                        System.out.println("\nVoce gostaria de perfurar o tanque do foguete?\n Lembre-se que isso causara danos irreversiveis e o foguete nunca mais podera acelerar");
                        System.out.println("1 - Sim");
                        System.out.println("2 - Nao");
                        do {
                            c = scan.nextInt();
                        } while (c != 1 && c != 2);
                        if (c == 1) {
                            fog.setFuel(false);
                            mfog.setFuel(false);
                        }
                    }
                    if (f == 4) {
                        fog.fire("Desintegrar");
                    }
                    if (f == 5) {
                        fog.statusFoguete();
                    }
                    break;
 
                }
                case 2: {
                    String str = mfog.getState();
                    System.out.println("O motor esta no estado " + mfog.getState());
                    System.out.println("\n1 - Abortar funcionamento do motor");
                    if (str.equals("estagio1") || str.equals("estagio2")) {
                        System.out.println("2 - Ligar proximo estagio do motor");
                        System.out.println("3 - Desligar um estagio do motor");
                        do {
                            mf = scan.nextInt();
                        } while (mf != 1 && mf != 2 && mf != 3);
                        if (mf == 1) {
                            mfog.fire("Abortar");
                        }
                        if (mf == 2) {
                            mfog.fire("Ligar");
                        }
                        if (mf == 3) {
                            mfog.fire("Desligar");
                        }
                    }
                    if (str.equals("desligado")) {
                        System.out.println("2 - Ligar o motor");
                        do {
                            mf = scan.nextInt();
                        } while (mf != 1 && mf != 2);
                        if (mf == 1) {
                            mfog.fire("Abortar");
                        }
                        if (mf == 2) {
                            mfog.fire("Ligar");
                        }
                    }
                    if (str.equals("estagio3")) {
                        System.out.println("2 - Desligar um estagio do motor");
                        do {
                            mf = scan.nextInt();
                        } while (mf != 1 && mf != 2);
                        if (mf == 1) {
                            mfog.fire("Abortar");
                        }
                        if (mf == 2) {
                            mfog.fire("Desligar");
                        }
                    }
                    break;
                }
            }
            if (fog.getState().equals("autoDestruicao")) {
                s = 0;
            }
        } while (s != 0);
    }
}

Foram executados diversos testes manuais e todos foram bem sucedidos. Seguem alguns dos testes executados:

TestFoguete1.jpg
TestFoguete2.jpg
TestFoguete3.jpg
TestFoguete4.jpg

Para encerrar o laboratório foi criado um teste na Classe Test.

Test


Todos os testes propostos foram bem sucedidos comprovando o bom funcionamento do pacote statemachine.

public class Teste {
 
    public Teste() {
    }
 
    @Test
    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) {
        }
    }
 
    @Test
    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) {
        }
    }
 
    @Test
    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) {
        }
    }
 
    @Test
    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) {
        }
 
    }
 
    @Test
    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) {
        }
    }
 
    @Test
    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;
    }
 
    @Test
    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) {
        }
 
    }
 
    @Test
    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) {
        }
    }
 
    @BeforeClass
    public static void setUpClass() throws Exception {
    }
 
    @AfterClass
    public static void tearDownClass() throws Exception {
    }
 
    @Before
    public void setUp() {
    }
 
    @After
    public void tearDown() {
    }
 
    // TODO add test methods here.
    // The methods must be annotated with annotation @Test. For example:
    //
    // @Test
    // public void hello() {}
}

Segue abaixo a tela com os testes executados.

A seguir estao os diagramas de classes do pacote statemachine, do pacote exemplo (contendo as classes professor e turma) e do pacote foguete.

DiagramadeClassesStateMachine.jpg
DiagramadeClassesExemplo.jpg
DiagramadeClassesFoguete.jpg
TestJUnit.jpg

Conclusão

O lab se mostrou bem trabalhoso e com uma grande variedade de detalhes (forte encapsulamento, classes anônimas, unchecked exceptions, padrão observer e etc.). Essas características levaram a um bom aprendizado dos novos conceitos abordados, tornando possível que nos próximos laboratórios outras características de java e de OO possam ser aprofundados. Um ponto negativo da atividade foi a grande quantidade de testes (foi testado o exemplo do professor-turma, um exemplo criado pelo aluno e uma classe de testes). Destaque para a dificuldade do teste que pedia a criação de uma máquina de estados, o que pode ser difícil para os graduandos com menor criatividade. Em suma a idéia do lab foi muito boa mas alguns pontos devem ser enchugados para não causar um trabalho repetitivo.

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