Lab3 Italo Aguiar

aluno: Italo Fernandes Aguiar
ano/sem: 2008/2o.
data do laboratório (3) : 13/10/2008 (2)

Introdução

Neste laboratório foi abordada, como ideia central, a concepcao e implementacao de uma maquina de estados a partir de uma descricao funcional dos comportamentos e ações que esta deve desempenhar. Não foi fornecido, como nos outros exercicios de laboratorio, um diagrama de classe com as principais classes envolvidas. Desta forma, tornou-se possivel exercitar o modelamento de um sistema por meio de um estudo dos seus requisitos.
Por meio das especificacoes e interfaces fornecidas juntamente com o modelamento de implementacao de classes desenvolvidos ao longo desta prática de laboratorio, desenvoveu-se uma biblioteca genérica que funciona de forma a atribuir um comportamento de maquina de estados para qualquer objeto.

Desenvolvimento

Tendo em vista a proposta do sistema em desenvolvimento, notou-se necessario a implementacao de 3 classes chaves, para representar um Evento, um Estado e uma Transicao. A abordagem usada nesta modelagem obedece a logica de que um estado pode estar relacionado a varios eventos e um evento pode estar relacionado com varios estados. A entidade que representa esse relacionamento de muitos para muitos e a entidade Transicao. Com base nisso, foram desenvolvidas todas as outras classes do sistema.
Assim, o seguinte Diagrama de Classes sintetiza como se dá o relacionamento entre todas as classes do pacote "stateMachine".

DiagramaDeClasses.jpg

As classes EventGuard, EventCommand, StateCommand e Observer permaneceram identicas ao fornecido nas especificacoes. Portanto, nos trechos seguintes encontram-se os codigos fonte apenas das classes desenvolvidas.

- pacote "stateMachine":

package stateMachine;
 
import java.util.Vector;
 
class Event {
    private StateMachine sm;
    private Vector<Transition> transitions = new Vector<Transition>();
    private EventGuard eventGuard = null;
    private EventCommand eventCommand;
 
    private String name;
 
    //  CONSTRUCTORS:
    public Event(StateMachine sm, String name) {
        this.sm = sm;
        this.name = name;
    }
 
    public Event(StateMachine sm, EventCommand eventCommand, String name) {
        this.sm = sm;
        this.eventCommand = eventCommand;
        this.name = name;
    }
 
    // GETTERS E SETTERS:
    public EventCommand getEventCommand() {
        return eventCommand;
    }
 
    public void setEventGuard(EventGuard eventGuard) {
        this.eventGuard = eventGuard;
    }
    public EventGuard getEventGuard() {
        return eventGuard;
    }
 
    public String getName() {
        return name;
    }
    public Vector<Transition> getTransitions() {
        return transitions;
    }    
 
    //  METODOS ADICIONAIS
    public void addTransition(Transition transition) {
        transitions.add(transition);
    }
 
}
package stateMachine;
 
import java.util.Vector;
 
class Operations {
 
    public static State nameToState( String name, Vector<State> states ) {
 
        for( State state : states) {
            if( state.getName().equals(name) ) return state;
        }
 
        throw new UndefinedStateException(name + " nao eh um estado definido nesta maquina de estados.");
    }
 
    public static Event nameToEvent( String name, Vector<Event> events ) throws UndefinedEventException {
 
        for( Event event : events) {
            if( event.getName().equals(name) ) return event;
        }
 
        throw new UndefinedEventException(name + " nao eh um evento definido");
    }
 
    public static Transition transitionFromEventAndCurrentState(Event event, State currentState) 
                                                            throws UndefinedTransitionException {
        //  PARA CADA TRANSACAO DO currentState VERIFICA-SE SE ESTA ESTA ASSOCIADA AO EVENTO Event        
        for( Transition transition : currentState.getTransitions() ) {
            //  CASO HAJA TAL ASSOCIACAO, ESTA EH RETORNADA
            if( transition.getEvent().equals(event) ) return transition;
        }
 
        // CASO NENHUMA TRANSACAO ESTEJA ASSOCIADA, LANCA-SE UMA EXCECAO
        throw new UndefinedTransitionException("Nao ha transicao de " + currentState.getName() 
                                                + " atraves do evento " + event.getName() + ".");
    }
 
}
package stateMachine;
 
import java.util.Vector;
 
class State {
 
    private Vector<Transition> transitions = new Vector<Transition>();
    private StateCommand stateCommand = null;
    private StateMachine stateMachine;
 
    private String name;
 
    //  CONSTRUCTORS:
    public State(StateMachine stateMachine, String name) {
        this.stateMachine = stateMachine;
        this.name = name;
    }
    public State(StateCommand stateCommand, StateMachine stateMachine, String name) {
        this.stateCommand = stateCommand;
        this.stateMachine = stateMachine;
        this.name = name;
    }
 
    //  GETTERS E SETTERS:
    public String getName() {
        return name;
    }
    public StateCommand getStateCommand() {
        return stateCommand;
    }
    public Vector<Transition> getTransitions() {
        return transitions;
    }   
 
    // METODOS ADICIONAIS
    public void addTransition( Transition transition ) {
        transitions.add(transition);        
    }
 
}
package stateMachine;
 
import java.util.Vector;
 
public class StateMachine{
 
    private Vector<Observer> observers = new Vector<Observer>();
    private Vector<State> states = new Vector<State>();
    private Vector<Event> events = new Vector<Event>();
    private State currentState = null;
 
    //  OBSEREVERS:
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    protected void notifyObservers() {
        for( Observer observer : observers) {
            observer.update(this, this.getCurrentStateName());
        }        
    }
 
    //  METODOS PARA A DEFINICAO DA MAQUINA
    public void createState(String name, StateCommand onEnter) {
        State state = new State(onEnter, this, name);
        states.add(state);
    }
    public void createState(String name) {
        State state = new State( this, name);
        states.add(state);
    }
 
    public void setInitialState(String name) {
        State newCurrentState = null;
 
        for( State state : states){
            if( state.getName().equals(name) ) { newCurrentState = state; }
        }
 
        this.currentState = newCurrentState;
 
        if(currentState == null) { /*   THROW UndefinedStateException */ }
    }
 
    public void createEvent(String name, String[] from, String to, EventCommand onSuccess) {
        try {
        //  CRIANDO EVENTO
        Event event = new Event(this, onSuccess, name);
        this.events.add(event);
 
        for( String fromName : from ) {
        //  RECUPERANDO OS ESTADOS INICIAIS E FINAIS
        //  ESTAS 2 INSTRUCOES PODEM GERAR UndefinedStateException ( NAO CHECADA )
        State fromState = Operations.nameToState(fromName, states);        
        State toState = Operations.nameToState(to, states);
 
        //  CRIANDO TRANSICAO
        //  SABENDO QUE JAH VAO SER AQUI REALIZADOS OS RELACIONAMENTOS NOS 2 SENTIDOS
        Transition transition = new Transition(fromState, toState, event);
        }
        } catch ( UndefinedStateException ex ) {
            ex.printStackTrace();
        }
    }
    public void createEvent(String name, String[] from, String to) {
        try {
        //  CRIANDO EVENTO
        Event event = new Event(this, name);
        this.events.add(event);
 
        for( String fromName : from ) {
        //  RECUPERANDO OS ESTADOS INICIAIS E FINAIS
        //  ESTAS 2 INSTRUCOES PODEM GERAR UndefinedStateException ( NAO CHECADA )
        State fromState = Operations.nameToState(fromName, states);        
        State toState = Operations.nameToState(to, states);
 
        //  CRIANDO TRANSICAO
        //  SABENDO QUE JAH VAO SER AQUI REALIZADOS OS RELACIONAMENTOS NOS 2 SENTIDOS
        Transition transition = new Transition(fromState, toState, event);
        }
 
        } catch ( UndefinedStateException ex ) {
            ex.printStackTrace();
        }
    }
    public void createEvent(String name, String from, String to, EventCommand onSuccess) {
        try {
        //  CRIANDO EVENTO
        Event event = new Event(this, onSuccess, name);
        this.events.add(event);
 
        //  OBTENDO E VERIFICANDO ESTADOS INICIAIS E FINAIS
        //  ESTAS 2 INSTRUCOES PODEM GERAR UndefinedStateException ( NAO CHECADA )
        State fromState = Operations.nameToState(from, states);        
        State toState = Operations.nameToState(to, states);        
 
        // CRIANDO TRANSICAO
        Transition transition = new Transition(fromState, toState, event);
 
        // DEFININDO OS RELACIONAMENTOS:
        fromState.addTransition(transition); 
            // transition, ao ser criada, jah foi linkada ao fromState e ao event
 
        } catch ( UndefinedStateException ex ) {
            ex.printStackTrace();
        }
    }
    public void createEvent(String name, String from, String to) {
        try{
        //  CRIANDO EVENTO
        Event event = new Event(this, name);
        this.events.add(event);
 
        //  RECUPERANDO OS ESTADOS INICIAIS E FINAIS
        //  ESTAS 2 INSTRUCOES PODEM GERAR UndefinedStateException ( NAO CHECADA )
        State fromState = Operations.nameToState(from, states);        
        State toState = Operations.nameToState(to, states);
 
        //  CRIANDO TRANSICAO
        //  SABENDO QUE JAH VAO SER AQUI REALIZADOS OS RELACIONAMENTOS NOS 2 SENTIDOS
        Transition transition = new Transition(fromState, toState, event);
        } catch ( UndefinedStateException ex ) {
            ex.printStackTrace();
        }
 
    }
 
    public void appendTransitionToEvent(String event, String fromState, String toState) {
        try {
            //  ESTA INSTRUCAO PODE GERAR UMA UndefinedEventException
            Event evt = Operations.nameToEvent(event, events);
 
            //  ESTAS 2 INSTRUCOES PODEM GERAR UMA UndefinedStateException ( NAO CHECADA )
            State initialState = Operations.nameToState(fromState, states);
            State finalState = Operations.nameToState(toState, states);
 
            Transition transition = new Transition(initialState, finalState, evt);
            initialState.addTransition(transition);
 
        } catch (UndefinedEventException ex) {
            ex.getMessage();
        }
 
    }
    public void appendTransitionToEvent(String event, String[] fromState, String toState) {
        for( String from : fromState) {
            this.appendTransitionToEvent(event, from, toState);
        }
 
    }
 
    public void setGuardToEvent(String event, EventGuard g) {
        try {
            //  ESTA INSTRUCAO PODE GERAR UMA UndefinedEventException
            Event evt = Operations.nameToEvent(event, events);
            //  CASO NAO HAJA EXCECAO, SETA-SE A CONDICAO DE GUARDA DO EVENTO
            evt.setEventGuard(g);
 
        } catch (UndefinedEventException ex) {
            ex.getMessage();
        }
    }
 
    //  METODOS PARA A EXECUCAO DA MAQUINA
    public boolean fireEvent(String name) throws UndefinedTransitionException, UndefinedEventException  {
//        try {
            //  ESTA INSTRUCAO PODE GERAR UMA UndefinedEventException
            Event event = Operations.nameToEvent(name, events);
 
            //  CHECANDO CONDICAO DE GUARDA
            if (event.getEventGuard() != null) {
                if (event.getEventGuard().shouldPerform() == false) {
                    return false;
                }
            }
 
            //  CASO A CONDICAO DE GUARDA SEJA OBEDECIDA OU NAO HAJA CONDICAO DE GUARDA:
            //  OBTEM-SE A TRANSICAO QUE SAI DO currentState ATRAVES DO EVENTO event.
            //  CASO A TRANSICAO NAO EXISTA, EH LANCADA UMA UndefinedTransitionException:
            Transition transition = Operations.transitionFromEventAndCurrentState(event, this.currentState);
 
            //  CASO EXISTA A TRANSICAO A PARTIR DO ESTADO currentState ATRAVES DO EVENTO event:
            //  EXECUTA-SE A ACAO DE EVENTO, MUDA-SE O ESTADO DA MAQUINA E  E NOTIFICA OS OBSERVADORES
            if (event.getEventCommand() != null) {
                event.getEventCommand().execute(this.currentState.getName());
            }
            this.currentState = transition.getFinalState();
 
            //  EXECUTA-SE A ACAO DE ESTADO CASO ELA EXISTA
            if (this.currentState.getStateCommand() != null) {
                this.currentState.getStateCommand().execute();
            }
 
            this.notifyObservers();
            return true;
 
//        } catch (UndefinedEventException ex) {
//            ex.printStackTrace();
//        }
 
//        return false;
    }
 
    public String getCurrentStateName() {
        return currentState.getName();
    }
 
}
package stateMachine;
 
class Transition {
    private State initialState;
    private State finalState;
    private Event event;
 
    private String name;
 
    //  CONSTRUCTORS:
    public Transition(State initialState, State finalState, Event event) {
        //  AS SEGUINTES 5 INSTRUCOES TRATAM DE REALIZAR O RELACIONAMENTO NOIS 2 SENTIDOS DAS ASSOCIACOES
        this.initialState = initialState;
        initialState.addTransition(this);
 
        this.finalState = finalState;
 
        this.event = event;
        event.addTransition(this);
 
        this.name = initialState.getName() +"("+ event.getName() +")"+ finalState.getName();
    }
 
    //  GETTERS E SETTERS:
    public Event getEvent() {
        return event;
    }
    public State getFinalState() {
        return finalState;
    }
    public State getInitialState() {
        return initialState;
    }
    public String getName() {
        return name;
    }
 
}
package stateMachine;
 
public class UndefinedEventException extends RuntimeException{
 
    public UndefinedEventException(String message) {
        super(message);
    }
 
    public UndefinedEventException() {
    }
 
}
package stateMachine;
 
public class UndefinedStateException extends RuntimeException {
 
    public UndefinedStateException(String message) {
        super(message);
    }
 
    public UndefinedStateException() {
    }
 
}
package stateMachine;
 
public class UndefinedTransitionException extends Exception{
 
    public UndefinedTransitionException() {
    }
 
    public UndefinedTransitionException(String message) {
        super(message);
    }
 
}

Para o exemplo dado em aula, em que o estado atual da turma influencia o do professor que por sua vez influencia na forma como este prepara aulas e provas, foi desenvolvido o seguinte algoritmo de teste para comprovar o funcionamento do pacote.

package exemplo;
 
public class TesteProfessorComTurma {
 
    public static void main(String[] args) {
        Professor prof = new Professor();
        Turma turma = new Turma();
 
        turma.registerObserver(prof);
 
        turma.fire("ser_atenta");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva());
 
        turma.fire("usar_note");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva());
 
        turma.fire("cancerizar");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva());
 
        turma.fire("dormir");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva());
 
        turma.fire("dormir");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva());
 
        turma.fire("cancerizar");
        System.out.println(prof.prepararAula());
        System.out.println(prof.fazerProva());
 
    }
 
}

Como resultado obteve-se a seguinte saída:

testeProfessorTurma

Foi criada tambem uma situacao hipotetica de aplicacao para a biblioteca de maquina de estados desenvolvida. Os estados, eventos e transicoes do exemplo criado se relacionam de acordo com a figura a seguir.

Estados.jpg

Dessa forma, foi criada uma classe chamada Individuo em que, já no proprio construtor da classe, é setada a maquina de estado para cada instancia de individuo. A classe Individuo tem o seguinte código-fonte:

package beberCairELevantar;
 
import stateMachine.*;
 
public class Individuo extends WithSM implements Observer {
 
    private String nome;
    private boolean temCarro = false;
    private boolean temDinheiroNaCarteira = false;
 
    public Individuo(String nome) {
        super();
        setUpSM();
        sm.registerObserver(this);
 
        this.nome = nome;
    }
 
    //  GETTERS E SETTERS
    public String getNome() {
        return nome;
    }
    public void setNome(String nome) {
        this.nome = nome;
    }
 
    public boolean isTemCarro() {
        return temCarro;
    }
    public void setTemCarro(boolean temCarro) {
        this.temCarro = temCarro;
    }
 
    public boolean isTemDinheiroNaCarteira() {
        return temDinheiroNaCarteira;
    }
    public void setTemDinheiroNaCarteira(boolean temDinheiroNaCarteira) {
        this.temDinheiroNaCarteira = temDinheiroNaCarteira;
    }
 
    //  METODO IMPLEMENTADO DA CLASSE Observer DO PACOTE stateMachine
    public void update(StateMachine sm, String newStateName) {
        System.out.println(nome + " estah no seguinte estado: " + newStateName + "\n");
    }
 
    //  METODO PARA DEFINIR A MAQUINA DE ESTADO
    private void setUpSM() {
        //criando estados
        sm.createState("Sobrio e Livre");
        sm.createState("Alegre");
        sm.createState("Detido");
        sm.createState("Bebado", new StateCommand(sm) {
            public void execute() {
                System.out.println("Que eh isso rapaz! Deixe comigo que eu sei exatamente o que eu estou fazendo!!");
            }
        } );
        sm.createState("Morto", new StateCommand(sm) {
            public void execute() {
                System.out.println("Estou partindo para estudar a geologia dos campos celestes!");
            }
        });
 
        sm.setInitialState("Sobrio e Livre");
 
        //  EVENTO "tomar cerveja"
        sm.createEvent("tomar cerveja", "Sobrio e Livre", "Alegre", new stateMachine.EventCommand(sm) {
            public void execute(String previousState) {
                System.out.println("Eu estava " + previousState + ". Mas vou tomar uma geladinha! TO PAGANDO!!");
            }
        });
 
        String[] from = {"Alegre", "Bebado"};
        sm.appendTransitionToEvent("tomar cerveja", from, "Bebado");
 
        //  EVENTO tomar cachaca
        String[] from2 = {"Sobrio e Livre", "Alegre", "Bebado"};
        sm.createEvent("tomar cachaca", from2, "Bebado", new stateMachine.EventCommand(sm) {
            public void execute(String previousState) {
                System.out.println("Essa desceu rasgando a garganta!!");
            }
        });
 
        // EVENTO dirigir
        sm.createEvent("dirigir", "Sobrio e Livre", "Sobrio e Livre");
        sm.appendTransitionToEvent("dirigir", "Alegre", "Detido");
        sm.appendTransitionToEvent("dirigir", "Bebado", "Morto");
        sm.setGuardToEvent("dirigir", new stateMachine.EventGuard() {
            public boolean shouldPerform(){
               return temCarro;  // SE ELE TEM CARRO ELE PODE DIRIGI-LO
            }
        });
 
        //  EVENTO subornar fiscal
        sm.createEvent("subornar o fiscal", "Detido", "Sobrio e Livre", new stateMachine.EventCommand(sm) {
            public void execute(String previousState) {
                System.out.println("Ei Fiscal, me ajude que eu te ajudo......");
            }
        });
        sm.setGuardToEvent("subornar o fiscal", new stateMachine.EventGuard() {
            public boolean shouldPerform(){
               return temDinheiroNaCarteira;  // SE ELE TEM CARRO ELE PODE DIRIGI-LO
            }
        });
 
    }
 
}

A seguinte classe foi desenvolvida apenas para simular uma situacao de um individuo sujeito a eventos:

package beberCairELevantar;
 
public class Testar {
 
    public static void main(String[] args) {
 
        Individuo ind = new Individuo("Fulano");
 
        ind.setTemCarro(true);
        ind.setTemDinheiroNaCarteira(true);
 
        ind.fire("tomar cerveja"); 
        ind.fire("dirigir"); 
 
        ind.fire("subornar o fiscal"); 
 
        ind.fire("tomar cachaca");
        ind.fire("dirigir");
    }
 
}

O seguinte resultado foi obtido para a simulacao de disparos de eventos para o individuo Fulano:

testeBeberCairELevantar

Para o conjunto de testes Junit fornecidos, obteve-se o seguinte resultado:

testesJUnit

Vale ressaltar que foi necessário definir a excecao de evento como extensao de RuntimeException, pois caso contrário não haveria harmonia com a interface fornecida para a classe StateMachine e nem com o conjunto de testes.

Conclusão

Nessa pratica de laboratorio foram abordados temas chaves para a programacao orientada a objetos como o uso de padroes ( ou design patterns ). O padrão Observer foi usado para a maquina de estados avisar ao seu "owner" cada vez ela mudar de estado, para este poder realizar alguma ação. Foi também utilizado o padrão Command, para implementar as açoes de Estado e de Evento.
O uso de tais padrões possibilitou uma solucao rapida e eficiente para algumas das necessidades que vieram a tona na implementacao do sistema em questao, evitando assim de ter de ficar "reinventando a roda" a todo momento.
O encapsulamento torna-se util para abstrair o programador que consome a biblioteca stateMachine de ter de operar a nivel de instanciar e manusear Estados, Transicoes e Eventos, pois apenas por meio de strings e metodos da classe StateMachine o programador é capaz de utilizar todas as ferramentas disponiveis pela biblioteca.
Portanto foi possivel constatar que diversas ferramentas e praticas da programacao orientada a objetos foram usadas na implementacao do sistema proposto nesta prática de laboratório, ajudando a atingir a meta de se estabelecer uma base solida de conhecimento nao em uma linguagem específica mas no conceitos gerais para este tipo de programacao.

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