aluno: Yves Conselvan
ano/sem: 2008/2o.
data do laboratório (num. da semana) : ???
Introdução
Neste laboratório nossa tarefa foi implementar uma biblioteca de maquina de estados finitos para utilizar em aplicações Java, e implementar uma aplicação de teste. Para esta tarefa fomos orientado a criar um pacote contendo todas as classes necessárias à maquina de estados.
Desenvolvimento
No desenvolvimento do pacote StateMachine utilizei a seguinte estrutura de classes.
A classe StateMachine é a classe principal do pacote, que manipula a criação de estados e eventos, as classes State e Event representam respectivamente um estado e um evento da maquina de estados.
Na classe StateMachine utilizei HashMap's para fazer a ligação entre nome de evento ou estado e o objeto do tipo Event ou State.
class StateMachine { ... private HashMap<String, State> states = new HashMap<String, State>(); private HashMap<String, Event> events = new HashMap<String, Event>(); ... }
Dentro da classe State ficam as informações sobre as transições que o estado pode executar. Utilizando um HashMap é feita a ligação entre nome do evento, e nome do estado para o qual irá transitar com aquele evento. Para fazer esta ligação criei o método appendTransition na classe State. Dessa forma ficou sem sentido a utilização de uma classe para representar as transições.
class State { ... StateCommand onEnter = null; HashMap<String, String> transitions = new HashMap<String, String>(); ... public void appendTransition(String eventName, String toState) { transitions.put(eventName, toState); } ... }
Utilizando essa estrutura a transição dos estados é feita utilizando o método fireEvent da classe State, que irá retornar o nome do estado para o qual a máquina deverá transitar. Caso não exista no HashMap nenhuma chave com o nome do evento será lançada uma exceção UndefinedTransitionException.
public String fireEvent(String eventName) throws UndefinedTransitionException { String nextStateName = null; if ((nextStateName = transitions.get(eventName)) == null) { throw new UndefinedTransitionException(); } return nextStateName; }
Na classe Event as ações de guarda podem ser adicionadas utilizando o método setGuard, e a classe possuiu o método verifyEventGuards que é chamado pela classe StateMachine ao se tentar disparar um evento.
class Event { ... public void setGuard(EventGuard g) { guards.add(g); } public boolean verifyEventGuards() { boolean canFire = true; for (EventGuard guard : guards) { if (!guard.shouldPerform()) { canFire = false; break; } } return canFire; } ... }
Os métodos stateEnter da classe State e eventSuccess da classe Event são chamados pelo objeto da classe StateMachine ao se transitar de estado.
class State { ... public void stateEnter() { if (onEnter != null) { onEnter.execute(); } } } class Event { ... public void eventSuccess(String previousEvent) { if (this.onSuccess != null) { this.onSuccess.execute(previousEvent); } } }
Função responsável pelo disparamento do evento na classe StateMachine
class StateMachine { ... public boolean fireEvent(String name) throws UndefinedTransitionException { boolean fired = false; Event event; String previousState; event = getEventByName(name); if (event.verifyEventGuards()) { if (currentState != null) { previousState = getCurrentStateName(); currentState = getStateByName(currentState.fireEvent(name)); currentState.stateEnter(); event.eventSuccess(previousState); fired = true; notifyObservers(); } } return fired; } ... }
Na classe StateMachine o método appendTransitionToEvent é o responśavel pela distribuição das relações "evento - estado de chegada" para cada estado.
Disponível com duas assinaturas uma apenas formata os dados na forma adequada para passar à função que efetivamente realiza o trabalho.
class StateMachine { ... public void appendTransitionToEvent(String event, String fromState, String toState) { String[] fromStates = new String[1]; fromStates[0] = fromState; appendTransitionToEvent(event, fromStates, toState); } public void appendTransitionToEvent(String event, String[] fromState, String toState) { State stateToAppend; for (String from : fromState) { if ((stateToAppend = getStateByName(from)) != null) { stateToAppend.appendTransition(event, toState); } } } ... }
O método setGuardToEvent da classe StateMachine apenas delega para o evento a adição da condição de guarda.
class StateMachine { ... public void setGuardToEvent(String event,EventGuard g) { Event eventToGuard; if ((eventToGuard = (Event)events.get(event)) != null) { eventToGuard.setGuard(g); } } ... }
Ainda na classe StateMachine criei métodos para retornar os objetos de Event e State a partir do nome. Inclui nestas funções o lançamento de exceçõs no caso de um evento ou estado não existir.
class StateMachine { ... private State getStateByName(String stateName) { State retState = null; if ((retState = states.get(stateName)) == null) { throw new UndefinedStateError(); } return retState; } private Event getEventByName(String eventName) { Event retEvent = null; if ((retEvent = events.get(eventName)) == null) { throw new UndefinedEventError(); } return retEvent; } }
Implementada a máquina de estados executei os testes e apresento abaixo o resultado e as saídas.
Saida:
Para teste da máquina de estados implementei a aplicação de exemplo com as classes Professor e Turma, e também implementei o outro exemplo.
Diagrama de estados da implementação feita
Conclusão
Na implementação da máquina de estados utilizamos o padrão Observer para trocar informação entre as entidades e não deixá-las muito acopladas. Para facilitar a utilização da máquina de estados foi utilizada herança a partir da classe WithSM