aluno: Fabio Imada
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 09/10/2008 (2)
Introdução
Neste laboratório foi projetada uma máquina de estados a partir de sua interface pública. Desse modo foi necessário modelar a aplicação para que esta cumprisse com determinados requisitos.
Como a interface pública da máquina de estados já havia sido definida e testada, foi interessante notar o forçado encapsulamento do pacote. Todas as funcionalidades desejáveis para o usuário já estavam definidas, restando ao desenvolvedor projetar apenas o mecanismo interno que realizaria tais funções.
Desenvolvimento
O desenvolvimento foi realizado em IDE NetBeans em torno da interface pública da classe StateMachine.
Baseando-se nas assinaturas dos métodos, o corpo dos métodos foram escritos.
Ao longo do código, foi surgindo a necessidade de novas classes e funções. Como estes não faziam parte da interface pública, o acesso para eles foi definido como package-protected (uma vez que seriam utilizados dentro do pacote). Alguns outros foram definidos como private, visto que só havia necessidade de utilizá-los dentro da própria classe (como as variáves de instância, por exemplo).
Surgiu então a necessidade de modelar, através de um diagrama de classes, a interação das novas classes entre si e com a StateMachine. O resultado obtido após algumas tentativas frustradas foi o diagrama de classes abaixo:
Desse modo, foi definido parcialmente uma interface para as novas classes e repetiu-se o processo de desenvolver a classe sobre a sua interface.
Segue abaixo os códigos que foram mais trabalhados, alguns como a classe abstrata StateCommand não foram citados por ser análogo ao EventCommand que foi dado.
++Classes
- Classes de Erro
As classes de erro foram implementadas automaticamente pelo NetBeans:
package stateMachine; public class UndefinedStateError extends RuntimeException { public UndefinedStateError() { } public UndefinedStateError(String msg) { super(msg); } }
- Classe Event
package stateMachine; import java.util.Vector; class Event { private String name; private StateMachine sm; private Vector<EventCommand> ecList = new Vector<EventCommand>(); private Vector<EventGuard> egList = new Vector<EventGuard>(); Event(StateMachine sm,String name){ this.name=name; this.sm=sm; } void addEventCommand(EventCommand ec){ ecList.add(ec); } void addEventGuard(EventGuard g) { egList.add(g); } boolean checkEventGuard(){ boolean teste=true; if(egList!=null){ for(EventGuard eg:egList){ if(eg.shouldPerform()==false) teste=false; } return teste; } else return true; } String getName() { return name; } void execute(String name){ if(ecList!=null) for(EventCommand ec:ecList) ec.execute(name); } }
- Classe Transition
package stateMachine; class Transition { private Event fireEvent; private State nextState; Transition (Event event,State state){ fireEvent=event; nextState=state; } Event getEvent(){ return fireEvent; } State getState(){ return nextState; } }
- Classe State
package stateMachine; import java.util.*; class State { private String name; private Vector<Transition> transitionList = new Vector<Transition>(); private StateMachine sm; private Vector<StateCommand> scList = new Vector<StateCommand>(); State(StateMachine sm,String name){ this.name=name; this.sm=sm; } void addTransition(String eventName,String nextState){ try{ Event event = sm.findEvent(eventName); State state = sm.findState(nextState); Transition transition = new Transition(event,state); transitionList.add(transition); } catch(UndefinedStateError e){ }catch(UndefinedEventError e){ } } void addStateCommand(StateCommand sc){ scList.add(sc); } String getName(){ return name; } State findTransition(Event eventFired) throws UndefinedEventError{ for(Transition t : transitionList){ if(eventFired == t.getEvent())return t.getState(); } throw new UndefinedEventError("Undefined transition."); } void execute(){ if(scList!=null){ for(StateCommand sc : scList) sc.execute(); } } }
- Classe StateMachine
package stateMachine; import java.util.*; public class StateMachine implements Subject { /*-----------------------Variaveis de instancia----------------------*/ private Vector<Observer> observerList =new Vector<Observer>(); private Vector<State> stateList = new Vector<State>(); private Vector<Event> eventList = new Vector<Event>(); private State state; /*----------------------Comportamento observavel---------------------*/ public void registerObserver(Observer observer){ observerList.add(observer); } public void notifyObservers(){ for(Observer observer : observerList){ observer.update(this,state.getName()); } } /*---------------Metodos para definicao da maquina-------------------*/ public void createState(String name){ State newState = new State(this,name); stateList.add(newState); if(state==null)state=newState; } public void createState(String name, StateCommand onEnter){ this.createState(name); try{ State editState = this.findState(name); editState.addStateCommand(onEnter); }catch(UndefinedStateError e){} } public void setInitialState(String name){ try{ state = this.findState(name); }catch(UndefinedStateError e){} } public void createEvent(String name, String from, String to){ Event newEvent = new Event(this,name); eventList.add(newEvent); try{ State editState = this.findState(from); editState.addTransition(name, to); } catch(UndefinedStateError e){ System.out.println("Undefined State in State Machine"); } } public void createEvent(String name, String from, String to, EventCommand onSuccess){ this.createEvent(name,from,to); try{ Event editEvent = this.findEvent(name); editEvent.addEventCommand(onSuccess); }catch(UndefinedEventError e){} } public void createEvent(String name, String[] from, String to){ for(String fromState : from){ this.createEvent(name,fromState,to); } } public void createEvent(String name, String[] from, String to, EventCommand onSuccess){ for(String fromState : from){ this.createEvent(name,fromState,to,onSuccess); } } public void appendTransitionToEvent(String event, String fromState, String toState){ try{ State editState = findState(fromState); editState.addTransition(event,toState); } catch(UndefinedStateError e){ } } public void appendTransitionToEvent(String event, String[] fromState, String toState){ for(String from : fromState){ appendTransitionToEvent(event,from,toState); } } public void setGuardToEvent(String event,EventGuard g){ try{ Event editEvent = findEvent(event); editEvent.addEventGuard(g); } catch(UndefinedEventError e){} } /*---------------Metodos para execucao da maquina-------------------*/ public boolean fireEvent(String name) throws UndefinedTransitionException{ try{ Event event = findEvent(name); if(event.checkEventGuard()){ State nextState = state.findTransition(event); previousState = state.getName(); state = nextState; state.execute(); event.execute(previousState); for(Observer obs : observerList){ obs.update( this ,state.getName() ); } } else{ for(Observer obs : observerList){ obs.update( this ,state.getName() ); } return false; } }catch(UndefinedEventError e){ throw new UndefinedTransitionException("Undefined event in state."); } return true; } /*--------------Metodos auxiliares (package-protected)----------------*/ State findState(String stateName) throws UndefinedStateError{ for(State testState : stateList){ if(stateName.equals(testState.getName() ) ){ return testState; } } throw new UndefinedStateError("Undefined state."); } Event findEvent(String eventName) throws UndefinedEventError{ for(Event testEvent : eventList){ if(eventName.equals(testEvent.getName() ) ){ return testEvent; } } throw new UndefinedEventError("Undefined event."); } }
Diagrama de Classes Final
Em BlueJ:
E em NetBeans:
Teste do Pacote stateMachine
Figura 4. Teste de funcionamento do pacote
Teste Professor-Turma
Classe
- testProfessorWithSM
import java.io.Console; public class testProfessorWithSM { public static void main(String[] args){ Console c = System.console(); Professor prof = new Professor(); Turma turma = new Turma(); turma.registerObserver(prof); String teste; do{ teste = c.readLine("O professor esta recebendo milhões? "); if(teste.equals("s")){ c.printf("O professor nunca fica desmotivado!\n"); prof.setGanhandoMilhoesDeDolares(true); } else prof.setGanhandoMilhoesDeDolares(false); String event = c.readLine("O que a turma ira fazer? "); turma.fire(event); c.printf( prof.fazerProva() + "\n" + prof.prepararAula() ); c.printf("\n"); teste = c.readLine("Continuar? "); c.printf("\n"); }while( teste.equals("s") ); } }
Resultado
O professor esta recebendo milhões? s
O professor nunca fica desmotivado!
O que a turma ira fazer? dormir
Prova com noção
Aula preparada com carinho (5 horas)
Continuar? s
O professor esta recebendo milhões? n
O que a turma ira fazer? dormir
Fiquei mais desmotivado!
Meu estado agora Ú desmotivado
Prova ruim, correção carteada
Aula preparada com pressa (1 hora)
Continuar? s
O professor esta recebendo milhões? n
O que a turma ira fazer? usar_note
Fiquei mais desmotivado!
nunca mais serei o mesmo!
Meu estado agora Ú irado
Prova sem noção, média D
Aula jogada (5 min)
Continuar? s
O professor esta recebendo milhões? n
O que a turma ira fazer? cancerizar
Oba, fiquei mais motivado!
Meu estado agora Ú vacinado
Prova ruim, correção carteada
Aula preparada com pressa (1 hora)
Continuar? n
Teste Rancho
Diagrama de Estados
Figura 5. Diagrama de Classes de Rancho
Classes
- WithSM (idem teste Professor-Turma)
- Rancho
public class Rancho extends WithSM implements stateMachine.Observer { public Rancho() { super(); setUpSM(); sm.registerObserver(this); } private boolean comiDobradinha = false; public boolean euComiDobradinha(){ return comiDobradinha; } public void setComiDobradinha(boolean is){ comiDobradinha = is; } public void update(stateMachine.Subject sm, String newStateName) { System.out.println("Eu vou " + newStateName); } private void setUpSM() { //criando estados sm.createState("comer no rancho",new stateMachine.StateCommand(sm){ public void execute(){ System.out.println("Vale o esforco pra ir pro kart!"); } }); sm.createState("comer no COCTA",new stateMachine.StateCommand(sm){ public void execute(){ System.out.println("O rancho tah osso!!!"); } }); sm.setInitialState("comer no COCTA"); //evento carne em cubos sm.createEvent("carne em cubos", "comer no rancho", "comer no COCTA"); sm.appendTransitionToEvent("carne em cubos","comer no COCTA","comer no COCTA"); //evento dobradinha sm.createEvent("dobradinha", "comer no rancho", "comer no COCTA", new stateMachine.EventCommand(sm) { public void execute(String previousState) { setComiDobradinha(true); System.out.println("Nunca mais serei o mesmo!!!"); } }); sm.appendTransitionToEvent("dobradinha", "comer no COCTA", "comer no COCTA"); //Event medalhao de file mignon sm.createEvent("medalhao de file mignon","comer no COCTA","comer no rancho"); sm.appendTransitionToEvent("medalhao de file mignon","comer no rancho","comer no rancho"); sm.setGuardToEvent("medalhao de file mignon", new stateMachine.EventGuard(){ public boolean shouldPerform(){ return !euComiDobradinha(); } }); } }
- testRanchoWithSM
import java.io.Console; public class testRanchoWithSM { public static void main(String[] args){ Console c = System.console(); Rancho rancho= new Rancho(); String teste; do{ c.printf("O que tem no rancho hoje?\n"); c.printf("\t1. Medalhao de file mignon\n"); c.printf("\t2. Carne em cubos\n"); c.printf("\t3. Dobradinha\n"); teste = c.readLine(); int numero = Integer.parseInt(teste); switch(numero){ case 1: rancho.fire("medalhao de file mignon"); break; case 2: rancho.fire("carne em cubos"); break; case 3: rancho.fire("dobradinha"); break; default: rancho.fire("carne em cubos"); break; } teste = c.readLine("Continuar? "); c.printf("\n"); }while( teste.equals("s") ); } }
Resultado
O que tem no rancho hoje?
1. Medalhao de file mignon
2. Carne em cubos
3. Dobradinha
1
Vale o esforco pra ir pro kart!
Eu vou comer no rancho
Continuar? s
O que tem no rancho hoje?
1. Medalhao de file mignon
2. Carne em cubos
3. Dobradinha
2
O rancho tah osso!!!
Eu vou comer no COCTA
Continuar? s
O que tem no rancho hoje?
1. Medalhao de file mignon
2. Carne em cubos
3. Dobradinha
3
Nunca mais serei o mesmo!!!
O rancho tah osso!!!
Eu vou comer no COCTA
Continuar? s
O que tem no rancho hoje?
1. Medalhao de file mignon
2. Carne em cubos
3. Dobradinha
1
Eu vou comer no COCTA
Continuar? n
Conclusão
A abordagem poderia ter sido menos apressada, pois isso economizaria tempo quando foi necessário modelar o diagrama de classes.
Do modo como foi feito, o entendimento do programa foi acompanhando o desenvolvimento da classe StateMachine, e por isso foi necessário reajustar o modelo diversas vezes para que este se adequasse à proposta.
Foi interessante notar que houve uma preocupação maior com o encapsulamento do pacote neste laboratório porque pela primeira vez foi utilizada uma interação entre pacotes, justificando o uso da permissão package-protected.
OBS. Após ver outros relas, foi adicionado a possibilidade de haverem múltiplos EventGuard e EventCommand por Event; e múltiplos StateCommand por State.