Lab3 Misael Alexandre

aluno: Misael Feitosa Alexandre
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 13/10/2008 (3)

Introdução

Este laboratório consistiu na criação de uma Máquina de Estados. Tal máquina foi desenvolvida aplicando vários conceitos dados em aula, tais como padrões de projeto (Observer), uso de interfaces e muitos outros. Tal máquina de estados foi encapsulada em um package, facilitando assim o uso da mesma para diversas aplicações.

Desenvolvimento

Para o desenvolvimento da máquina de estados, partiu-se do esqueleto da classe StateMachine dada nas intruções, que foi:

//comportamento de observável
    public void registerObserver(Observer observer);
    protected void notifyObservers();
    //métodos para a definição da máquina
    public void createState(String name, StateCommand onEnter);
    public void createState(String name);
    public void setInitialState(String name);
    public void createEvent(String name, String[] from, String to, EventCommand onSuccess);     
    public void createEvent(String name, String[] from, String to);
    public void createEvent(String name, String from, String to,  EventCommand onSuccess);
    public void createEvent(String name, String from, String to);
    public void appendTransitionToEvent(String event, String fromState, String toState);
    public void appendTransitionToEvent(String event, String[] fromState, String toState);
    public void setGuardToEvent(String event,EventGuard g);
   // métodos para a execução da máquina
    public boolean fireEvent(String name) throws UndefinedTransitionException;
    public String getCurrentStateName();

Após o total desenvolvimento da classe StateMachine, o código da mesma foi:

package stateMachine;
 
import java.util.*;
 
public class StateMachine
{
    State currentState;
    private Vector<Observer> observers=new Vector<Observer>();
    private Vector<State> states=new Vector<State>();
    private Vector<Event> events=new Vector<Event>();
 
    //comportamento de observável
    public void registerObserver(Observer observer){
        observers.add(observer);
    }
 
    protected void notifyObservers(){
        for(Observer obs : observers){
            obs.update(this,getCurrentStateName());
        }
    }
 
    //métodos para a definição da máquina
    public void createState(String name, StateCommand onEnter){
        boolean ok=true;
        for (State s : states){
            if(s.getName()==name) ok=false;
        }
        if(ok){
           State s=new State(name,onEnter);
           states.add(s);
        }
        else{
            throw new ExistingStateException("Este estado ja existe");
        }
    }
 
    public void createState(String name){
        boolean ok=true;
        for (State s : states){
            if(s.getName()==name) ok=false;
        }
        if(ok){
           State s=new State(name);
           states.add(s);
        }
        else{
            throw new ExistingStateException("Este estado ja existe");
        }
    }
 
    public void setInitialState(String name){
        boolean ok=false;
        for(State st : states){
            if(st.getName()==name){
                this.currentState=st;
                currentState.sc.execute();
                ok=true;
            }
        }
        if(!ok) throw new UndefinedStateException("Estado inexistente");
    }
 
    public void createEvent(String name, String[] from, String to, EventCommand onSuccess){
        boolean ok=true;
        ok=existEvent(name);
        if(ok){
            throw new ExistingEventException("O evento "+name+" ja existe!");
        }
 
        for(String s : from){
            ok=existState(s);
            if(!ok){
                throw new UndefinedStateException("O estado "+s+" nao existe!");
            }
        }
 
        ok=existState(to);
        if(!ok){
            throw new UndefinedStateException("O estado "+to+" nao existe!");
        }
 
        if(ok){
            Event v=new Event(name,from,to,onSuccess);
            events.add(v);
        }
    }
 
    public void createEvent(String name, String[] from, String to){
        boolean ok=true;
        ok=existEvent(name);
        if(ok){
            throw new ExistingEventException("O evento "+name+" ja existe!");
        }
 
        for(String s : from){
            ok=existState(s);
            if(!ok){
                throw new UndefinedStateException("O estado "+s+" nao existe!");
            }
        }
 
        ok=existState(to);
        if(!ok){
            throw new UndefinedStateException("O estado "+to+" nao existe!");
        }
 
        if(ok){
            Event v=new Event(name,from,to);
            events.add(v);
        }
    }
 
    public void createEvent(String name, String from, String to,  EventCommand onSuccess){
        boolean ok=true;
        ok=existEvent(name);
        if(ok){
            throw new ExistingEventException("O evento "+name+" ja existe!");
        }
 
        ok=existState(from);
        if(!ok){
            throw new UndefinedStateException("O estado "+from+" nao existe!");
        }
 
        ok=existState(to);
        if(!ok){
            throw new UndefinedStateException("O estado "+to+" nao existe!");
        }
 
        if(ok){
            Event v=new Event(name,from,to,onSuccess);
            events.add(v);
        }
    }
 
    public void createEvent(String name, String from, String to){
        boolean ok=true;
        ok=existEvent(name);
        if(ok){
            throw new ExistingEventException("O evento "+name+" ja existe!");
        }
 
        ok=existState(from);
        if(!ok){
            throw new UndefinedStateException("O estado "+from+" nao existe!");
        }
 
        ok=existState(to);
        if(!ok){
            throw new UndefinedStateException("O estado "+to+" nao existe!");
        }
 
        if(ok){
            Event v=new Event(name,from,to);
            events.add(v);
        }
    }
 
    public void appendTransitionToEvent(String event, String fromState, String toState) throws UndefinedEventException{
        boolean ok=false;
        int eventIndex=0;
        for (Event e : events){
            if(e.getName()==event){
                ok=true;
                eventIndex=events.indexOf(e);
            }
        }
        if(!ok){
           throw new UndefinedEventException("O evento "+event+" nao existe!");
        }
 
        for (State st : states){
            if(st.getName()==fromState){
                ok=true;
            }
        }
        if(!ok){
           throw new UndefinedStateException("O estado "+fromState+" nao existe!");
        }
 
        for (State st : states){
            if(st.getName()==toState){
                ok=true;
            }
        }
        if(!ok){
           throw new UndefinedStateException("O estado "+toState+" nao existe!");
        }
 
        if(ok){
            events.get(eventIndex).appendTransitions(fromState,toState);
        }
    }    
 
    public void appendTransitionToEvent(String event, String[] fromState, String toState) throws UndefinedEventException{
        boolean ok=false;
        int eventIndex=0;
        for (Event e : events){
            if(e.getName()==event){
                ok=true;
                eventIndex=events.indexOf(e);
            }
        }
        if(!ok){
           throw new UndefinedEventException("O evento "+event+" nao existe!");
        }
 
        for(String s : fromState){
            for (State st : states){
                if(st.getName()==s){
                    ok=true;
                }
            }
        }
        if(!ok){
           throw new UndefinedStateException("Ha um estado no vetor que nao existe!");
        }
 
        for (State st : states){
            if(st.getName()==toState){
                ok=true;
            }
        }
        if(!ok){
           throw new UndefinedStateException("O estado "+toState+" nao existe!");
        }
 
        if(ok){
            for(String s : fromState){    
                events.get(eventIndex).appendTransitions(s,toState);
            }
        }
    }
 
    public void setGuardToEvent(String event,EventGuard g) throws UndefinedEventException{
        boolean ok=false;
        int eventIndex;
        for (Event e : events){
            if(e.getName()==event){
                ok=true;
                eventIndex=events.indexOf(e);
                e.setGuard(g);
            }
        }
        if(!ok){
           throw new UndefinedEventException("O evento "+event+" nao existe!");
        }
    }
 
   // métodos para a execução da máquina
    public boolean fireEvent(String name) throws UndefinedTransitionException, UndefinedEventException {
        boolean ok=false, ok2=false, okfinal=false,pass=true;
        for (Event e : events){
            if(e.getName()==name){
                ok=true;
                for(Transition t : e.getTransitions()){
                    if((t.getOrigStateName()==currentState.getName())&&pass){
                        ok2=true;
                        if(e.getGuard().shouldPerform()){
                            e.eventCommand.execute(currentState.getName());
                            okfinal=true;
                            currentState=getState(t.getToStateName());
                            notifyObservers();
                            currentState.sc.execute();
                            pass=false;
                        }
                    }
                }
           }
 
        }
        if(!ok){
           throw new UndefinedEventException("O evento "+name+" nao existe!");
        }
        if(!ok2){
           throw new UndefinedTransitionException("Nao ha transicao possivel para este o estado atual");
        }
        return okfinal;
    }
 
    public String getCurrentStateName(){
        return currentState.getName();
    }
 
    private boolean existState(String name){
        boolean ok=false;
        for (State st : states){
            if(st.getName()==name){
                ok=true;
            }
        }
        return ok;
    }
 
    private boolean existEvent(String name){
        boolean ok=false;
        for (Event ev : events){
            if(ev.getName()==name){
                ok=true;
            }
        }
        return ok;
    }
 
    private State getState(String name){
        boolean ok=false;
        State state=new State(null);
        for(State s : states){
            if(s.getName()==name){
                ok=true;
                state=s;
            }
        }
        return state;
    }
 
}

Passos para o Desenvolvimento

Inicialmente, a fim de criar os métodos notifyObservers e registerObservers criou-se a interface Observer, a qual tinha a função de avisar às classes que observassem a máquina de estados o acontecimento de uma transição bem sucedida. O esqueleto da interface Observer pode ser visto abaixo:

package stateMachine;
 
//Cria um observer para a máquina de estados
 
public interface Observer
{
    void update(StateMachine sm, String newStateName);
 
}

Em seguida, foram criadas a classe State e a classe abstrata StateCommand, para que fossem implementados os métodos createState e setInitialState. A classe State tem construtores tanto para criação com um StateCommand como para uma criação sem o mesmo. A classe abstrata StateCommand aplica o padrão de projeto Command, sendo seu método execute executado sempre que uma transição para o determinado estado for bem sucedida. A criação do objeto a ser mandado, como pode ser visto nos testes, é feita através de classes anônimas.

Abaixo podem ser vistos os códigos de State e StateCommand:

package stateMachine;
 
public class State
{
    private String name;
    public StateCommand sc;
 
    //Construtor que não recebe StateCommand
    public State(String name)
    {
        this.name=name;
 
        //cria StateCommand vazio
        this.sc=new StateCommand(null){
            public void execute(){
            }
        };
    }
 
    //Construtor que recebe StateCommand
    public State(String name, StateCommand sc)
    {
        this.name=name;
        this.sc=sc;
    }
 
    public String getName(){
        return this.name;
    }
}
package stateMachine;
 
public abstract class StateCommand
{
    protected StateMachine machine;
    public StateCommand(StateMachine machine)
    {
        this.machine = machine;
    }
    public abstract void execute();
}

Logo após também foram criadas a classe Event, a classe abstrata EventCommand e a interface EventGuard. Na classe Event foram criados construtores para diversos tipos de entradas (um ou mais estados de origem, tendo ou não um EventCommand). A interface EventGuard, sendo seu objeto também criado através de classe anônima, coloca restrições para a execução de um determinado Evento.

Para que cada evento pudesse ter um registro de todas as suas possíveis transições de estado, foi criada uma classe Transicao, a qual tem dois atributos que se referem a um estado de origem e um estado de destino.

Através da criação das classes, classes abstratas e interfaces acima citadas, foi possível criar os métodos createEvent, appendTransitionToEvent e setGuardToEvent. O código das entidades pode ser visto abaixo:

package stateMachine;
 
import java.util.*;
 
public class Event
{
    private String name;
    public EventCommand eventCommand;
    private Vector<Transition> transitions=new Vector<Transition>();
    public EventGuard guard=new EventGuard(){
        public boolean shouldPerform(){
            return true;
        }
    };
 
    //Diferentes construtores
    public Event(String name, String[] from, String to, EventCommand ec){
        this.name=name;
        for(String s : from){
            this.transitions.add(new Transition(s,to));
        }
        eventCommand=ec;
    }
 
    public Event(String name, String from, String to, EventCommand ec){
        this.name=name;
        this.transitions.add(new Transition(from,to));
        eventCommand=ec;
    }
 
    public Event(String name, String[] from, String to){
        this.name=name;
        for(String s : from){
            this.transitions.add(new Transition(s,to));
        }
        eventCommand=new EventCommand(null){
            public void execute(String s){}
        };
    }
 
    public Event(String name, String from, String to){
        this.name=name;
        this.transitions.add(new Transition(from,to));
        eventCommand=new EventCommand(null){
            public void execute(String s){}
        };
    }
 
    //retorna o nome do event
    public String getName(){
        return this.name;
    }    
 
    //Adiciona um transicao
    public void appendTransitions(String fromState,String toState){
        this.transitions.add(new Transition(fromState, toState));
    }
 
    //seta um EventGuard para o evento
    public void setGuard(EventGuard g){
        this.guard=g;
    }
 
    //retorna o vetor de transicoes do evento
    public Vector<Transition> getTransitions(){
        return this.transitions;
    }
 
    public EventGuard getGuard(){
        return this.guard;
    }
}
package stateMachine;
 
public abstract class EventCommand
{
    protected StateMachine machine;
    public EventCommand(StateMachine machine)
    {
        this.machine = machine;
    }
    public abstract void execute(String previousState);
}
package stateMachine;
 
public interface EventGuard
{
   boolean shouldPerform();
}
package stateMachine;
 
public class Transition
{
    private String origState, toState;
    public Transition(String org, String to){
        this.origState=org;
        this.toState=to;
    }
    public String getOrigStateName(){
        return origState;
    }
 
    public String getToStateName(){
        return toState;
    }
}

Logo após foi desenvolvido o método fireEvent da classe StateMachine. Este método é o responsável pela transição de um estado para outro. Tal método verifica se o evento "fired" existe, e existindo verifica se existe uma transição possível. Sendo a transição possível, além de modificar o estado atual da máquina, o método notifica a mudança aos observadores e chama os métodos execute de StateCommand e EventCommand.

Por fim criou-se o método getCurrentStateName, que retorna o estado atual da máquina. Os demais métodos tiveram somente caráter auxiliar.

Vale salientar que foram feitas mudanças no conjunto de excessões propostas. O métodos fireEvent, além de poder lançar uma UndefinedTransitionException, também lançou pôde um UndefinedEventException. Ambas extendiam a classe Exception.

Além disso, foram também criadas excessões que extendiam a classe RuntimeException, que foram:

  • UndefinedStateException
  • ExistingStateException
  • ExistingEventException

Tais excessões foram usadas nos métodos createState, setInitialState, createEvent, appendTransitionToEvent e setGuardToEvent quando necessárias.

Diagrama de classes

O diagrama de classes da máquina final foi:

diagrama_classes.jpg

Testes

Foi criada uma classe de testes para rodar os testes dados na intrução do laboratório. Foram feitas pequenas modificações SOMENTE na parte de excessões, para adequação às alterações feitas. O código de testes pode ser visto abaixo:

package stateMachine;
 
public class Test extends junit.framework.TestCase
{
   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");
        }
 
        catch (UndefinedEventException e) {
        }
 
        assertEquals("feliz", sm.getCurrentStateName());
 
        try {
            sm.fireEvent("jogar_bola");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }
 
        catch (UndefinedEventException 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");
        }
 
        catch (UndefinedEventException e) {
        }
 
        assertEquals("deprimido", sm.getCurrentStateName());
        try {
            sm.fireEvent("perder_mulher");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }
 
        catch (UndefinedEventException 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");
        }
 
        catch (UndefinedEventException e) {
        }
        assertEquals("deprimido", sm.getCurrentStateName());
        try {
            sm.fireEvent("perder_mulher");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }
 
        catch (UndefinedEventException 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");
 
        try{
           sm.appendTransitionToEvent("perder_mulher", "super_feliz", "triste");
        }
        catch (UndefinedEventException e) {
        }
 
        try {
            sm.fireEvent("perder_mulher");
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
 
        catch (UndefinedEventException e) {
        }
        assertEquals("triste", sm.getCurrentStateName());
 
        try {
            sm.fireEvent("perder_mulher");
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
 
        catch (UndefinedEventException e) {
        }
        assertEquals("deprimido", sm.getCurrentStateName());
 
        try {
            sm.fireEvent("perder_mulher");
            fail("deve gerar excessão");
        }catch (UndefinedTransitionException e) {
        }
 
        catch (UndefinedEventException 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) {
        }
 
        catch (UndefinedEventException 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) {
        }
 
        catch (UndefinedEventException 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");
 
        try{
            sm.setGuardToEvent("perder_mulher", new EventGuard() {
                public boolean shouldPerform() {
                    return !temSuperPoderes;
                }
            });
        }
 
        catch (UndefinedEventException e) {
        }
 
        try {
            assertFalse(sm.fireEvent("perder_mulher"));
        }catch (UndefinedTransitionException e) {
        }
 
        catch (UndefinedEventException e) {
        }
 
        temSuperPoderes = false;
        try {
            assertTrue(sm.fireEvent("perder_mulher"));
        }catch (UndefinedTransitionException e) {
            fail("não deve gerar excessão");
        }
 
        catch (UndefinedEventException e) {
        }
    }
 
    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", "feliz");
 
        try {
            sm.fireEvent("evento_nao_existente");
            fail("deve gerar excessão");
        }
 
        catch (UndefinedEventException e) {
            System.out.println("Evento não existe!!!");
        }
        catch (UndefinedTransitionException 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(UndefinedTransitionException e) {
            System.out.println("Transicao impossivel!!!");
        }
 
        catch (UndefinedEventException e) {
        }
    }
 
}

Os resultados dos testes podem ser vistos nas figuras abaixo:

test_results.jpgtest_terminal.jpg

Convém salientar que o penúltimo teste não deu erro já que foi tratada a excessão (inexistencia do evento), o que pode ser visto no terminal através da última mensagem escrita. No útimo teste, o erro dado foi um RuntimeExeption que aconteceu na criação do próprio evento, diferentemente do que era esperado.

Isso foi resultado da postura adotada de "cortar o mal desde o nascimento", ou seja, impedir erros tais na própria criação do evento.

Aplicações

Sala de Aula

Em questão de aplicação da máquina de estados feita, inicialmente seu funcionamento foi testado na aplicação dada em sala
de aula. Os códigos das classes Professor e Turma e da interface TurmaObserver foram dados nas instruções de laboratório.
O código da classe ClassCenario (método main) pode ser visto abaixo:

public class ClassCenario
{
    public static void main(String[] args){
        Professor p=new Professor();
        Turma t=new Turma();
        t.registerObserver(p);
 
        //a turma tenta ficar atenta
        System.out.println("O estado do professor eh "+p.getState());
        t.fire("ser_atenta");
        System.out.println("O estado do professor eh "+p.getState());
        t.fire("dormir");
 
        //o professor passa a ganhar milhoes
        p.setGanhandoMilhoesDeDolares(true);
        System.out.println("O professor ganha milhoes!!!");
 
        //nada o faz ficar desmotivado
        System.out.println("O estado do professor eh "+p.getState());
        t.fire("usar_note");
        System.out.println("O estado do professor eh "+p.getState());
        t.fire("dormir");
        System.out.println("O estado do professor eh "+p.getState());
 
        //o professor naum ganha mais milhoes
        p.setGanhandoMilhoesDeDolares(false);
        System.out.println("O professor nao ganha milhoes!!!");
 
        //agora ele vai conseguir ficar irado
        t.fire("usar_note");
        System.out.println("O estado do professor eh "+p.getState());
        t.fire("dormir");
        System.out.println("O estado do professor eh "+p.getState());
        t.fire("usar_note");
        System.out.println("O estado do professor eh "+p.getState());
 
        //nunca mais voltara a ser motivado
        t.fire("cancerizar");
        System.out.println("O estado do professor eh "+p.getState());
        t.fire("usar_note");
        System.out.println("O estado do professor eh "+p.getState());
        t.fire("ser_atenta");
        System.out.println("O estado do professor eh "+p.getState());
    }
}

O resultado do método main da classe ClassCenario pode ser visto abaixo:

class_terminal.jpg

Hide Bomb

A aplicação criada foi a seguinte:

Imagine uma trava eletrônica para uma porta com controle remoto. Inicialmente, ela parece simples. Apertando o botão abrir, a porta abre. Apertando o botão fechar, a porta fecha. Entretanto, mal sabe as pessoas que há uma bomba escondida e que o código para a explodir é simples: fechar, fechar, abrir, abrir. Será que a bomba vai funcionar?

O esquema do BlueJ para o conjunto criado foi o seguinte:

bluj_bomb.jpg

O código das classes pode ser visto abaixo:

import stateMachine.*;
 
public class Portao extends WithSM implements stateMachine.Observer
{
 
    public Portao()
    {
        super();
        setUpSM();
        sm.registerObserver(this);
    }
 
    public void update(StateMachine s_machine, String stateName){
        if(stateName=="fech_1"||stateName=="fech_2"||stateName=="fech_3"){
            System.out.println("Agora o portao esta fechado");
        }
        else if(stateName=="aber_1"||stateName=="aber_2"){
            System.out.println("Agora o portao esta aberto");
        }
        else{
           System.out.println("Agora o portao esta explodido");
        }
       // System.out.println("Agora o portao esta "+stateName);
    }
 
    public void setUpSM(){
        sm.createState("fech_1");
        sm.createState("aber_1");
        sm.createState("fech_2");
        sm.createState("fech_3");
        sm.createState("aber_2");
        sm.createState("explodido");
 
        sm.setInitialState("fech_1");
 
        sm.createEvent("abrir", "fech_1","aber_1");
        sm.createEvent("fechar", "fech_1", "fech_2");
 
        try{
            sm.appendTransitionToEvent("fechar","aber_1","fech_2");
            sm.appendTransitionToEvent("fechar","fech_2","fech_3");
            String[] from={"fech_3","aber_2"};
            sm.appendTransitionToEvent("fechar",from,"fech_1");
            sm.appendTransitionToEvent("fechar","explodido","explodido");
 
            String[] from2={"aber_1","fech_2"};
            sm.appendTransitionToEvent("abrir",from2,"aber_1");
            sm.appendTransitionToEvent("abrir","fech_3","aber_2");
            String[] from3={"aber_2","explodido"};
            sm.appendTransitionToEvent("abrir",from3,"explodido");
        }
        catch(UndefinedEventException e){
        }
    }
 
    public void press_fechar(){
        fire("fechar");
    }
 
    public void press_abrir(){
        fire("abrir");
    }
 
    public String getState(){
        String stateName=sm.getCurrentStateName();
        if(stateName=="fech_1"||stateName=="fech_2"||stateName=="fech_3"){
            stateName="Agora o portao esta fechado";
        }
        else if(stateName=="aber_1"||stateName=="aber_2"){
            stateName="Agora o portao esta aberto";
        }
        else{
           stateName="Agora o portao esta explodido";
        }
        return stateName;
    }
 
}
public class Portao_Cenario
{
    public static void main(String[] args){
 
        Portao p=new Portao();
        //mostra o estado inicial do portao
        System.out.println(p.getState());
 
        //comecamos a abrir e fechar o portao
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_fechar();
        p.press_fechar();
        p.press_abrir();
        p.press_abrir();
        p.press_abrir();
        p.press_abrir();
        p.press_abrir();
        p.press_fechar();
        p.press_fechar();
        p.press_fechar();
        p.press_abrir();
 
        //um terrorista que sabe a senha vai explodir o portao
        p.press_fechar();
        p.press_fechar();
        p.press_abrir();
        p.press_abrir();
 
        //o portao deve continuar explodido
        p.press_abrir();
        p.press_fechar();
        p.press_fechar();
        p.press_fechar();
        p.press_abrir();
        p.press_fechar();
        p.press_fechar();
        p.press_abrir();
        p.press_abrir();
    }
}

Por fim, o resultado no terminal foi:

terminal_bomb.jpg

Notou-se assim que a bomba explodiu como esperado.

O arquivo zip com o projeto do BlueJ pode ser baixado pelo link abaixo:

Laboratório 3

Conclusão

Pode-se concluir que o laboratório foi um sucesso. A diferença básica da abordagem proposta em relação à minha abordagem foi o tratamento de excessões, que se mostrou diferente, como já explicitado. Foram usados dois padrões de projeto: Observer e Command. O primeiro, já conhecido, é essencial para algo como uma máquina de estados, já que diversos objetos "necessitam" saber de mudanças em outros. O padrão Command, este já novo, trouxe um conceito novo: execução de algo quando houver uma transição bem feita. O uso de classes anônimas (também conceito novo) se mostrou muito útil, pois pôde-se criar a classe no main. O encapsulamento foi essencial para a estética e bom uso da biblioteca por um usuário externo, já que o mesmo só tem contato com a classe StateMachine, a qual controla as outras. Juntar todas as classe em um só pacote ajuda também (separa a biblioteca da aplicação).

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