Lab3 Rafael Siqueira

aluno: Rafael Siqueira
ano/sem: 2008/2o.
data do laboratório (num. da semana) : 11/10/2008 (3)

Introdução

Nesse terceiro laboratório, implementamos em Java uma máquina de estados movida a eventos. Para tal, nos foi fornecida a interface da classe máquina de estados e a descrição de como essa funciona. Trata-se de uma máquina de estados finita, determinista, movida a eventos, a qual possui vários estados e transições entre os mesmos. Ao se disparar um evento, a máquina sai de um estado e vai para outro. Por ser determinista, de cada estado da máquina só pode sair no máximo uma transição que responde a um determinado evento. Além disso, a máquina pode possuir condições de guarda, ações de estado e ações de evento. As condições de guardas são cláusulas que os eventos podem possuir que impedem que os mesmos ocorram caso o resultado para as mesmas seja falso. As ações de estado são ações que ocorrem quando entra-se num novo estado cuja ação não seja nula. E por fim, as ações de evento são aquelas que são realizadas quando o evento é disparado e passa-se a condição de guarda.

Objetivos

Com o desenvolvimento da máquina, temos como objetivos:
- Ganhar mais traquejo no desenvolvimento de projetos em linguagens OO
- Aplicar o conhecimento adquirido em classe em com a prova de padrões de projeto, com a implementação do Observer e do Command
- Aprender novas ferramentas da linguagem, através do uso de sua vasta API
- Pôr em prática a noção de classes anônimas

Desenvolvimento

Em primeiro lugar, segue abaixo o diagrama das classes que compõem o pacote da máquina de estados (stateMachine)

fig1.JPG

Fig. 1 - Diagrama de classes da máquina de estados

E abaixo seguem os resultados dos testes

fig2.JPG

Fig. 2 - Resultados dos testes

Abaixo seguem os códigos das classes que considero principais no laboratório: StateMachine e Event.

Classe StateMachine

package stateMachine;
 
import java.util.*;
 
public class StateMachine 
{    
    private Vector<Observer> observers = new Vector<Observer>();
    private Dictionary<String,State> states = new Hashtable<String,State>();
    private Dictionary<String,Event> events = new Hashtable<String,Event>();
    State currentState;
    State previousState;
 
    //Comportamento de Observável
    public void registerObserver(Observer observer) 
    {
        observers.add(observer);
    }
 
    protected void notifyObservers() 
    {
        String currentStateName = getCurrentStateName();
        for (Observer obs : observers) 
        {
            obs.update(this,currentStateName);
        }
    }
    //Métodos para a Definição da Máquina
    public void createState(String name, StateCommand onEnter)
    {
        State state = new State(name,this);
        state.setStateCommand(onEnter);
        states.put(name,state);
    }
    public void createState(String name) 
    {
        State state = new State(name,this);
        state.setStateCommand(null);
        states.put(name,state);
    }
    public void setInitialState(String name) 
    {
        currentState = states.get(name);
    }
 
    public void createEvent(String name, String fromStateName, String toStateName, EventCommand onSuccess)
    {
        if (events.get(name) != null) 
        {   
            appendTransitionToEvent(name,fromStateName,toStateName);
        }
        else 
        {
        Event event = new Event(name,this);
        event.setEventCommand(onSuccess);
        events.put(name,event);
        State fromState = null;
        fromState = states.get(fromStateName);
        State toState = null;
        toState = states.get(toStateName);
        Transition transition = new Transition(fromState,toState,this);
        event.transitions.add(transition);
        }
    }
 
    public void createEvent(String name, String[] fromStateName, String toStateName, EventCommand onSuccess)
    {
        for (String fromState : fromStateName)
        {
            createEvent(name,fromState,toStateName,onSuccess);
        }
    }     
 
    public void createEvent(String name, String fromStateName, String toStateName)
    {
        if (events.get(name) != null) 
        {
            appendTransitionToEvent(name,fromStateName,toStateName);
        }
        else
        {
            Event event = new Event(name,this);
            event.setEventCommand(null);
            events.put(name,event);
            State fromState = null;
            fromState = states.get(fromStateName);
            State toState = null;
            toState = states.get(toStateName);
            Transition transition = new Transition(fromState,toState,this);
            event.transitions.add(transition);
        }
    }
 
    public void createEvent(String name, String[] fromStateName, String toStateName)
    {
        for (String fromState : fromStateName)
        {
            createEvent(name,fromState,toStateName);
        }
    }
 
    public void appendTransitionToEvent(String eventName, String fromStateName, String toStateName) 
    {
        Event editedEvent = events.get(eventName);
        State fromState = null;
        fromState = states.get(fromStateName);
        State toState = null;
        toState = states.get(toStateName);
        Transition transition = new Transition(fromState,toState,this);
        editedEvent.transitions.add(transition);
    }
    public void appendTransitionToEvent(String eventName, String[] fromStateName, String toStateName) 
    {
        for (String fromState : fromStateName) 
        {
            appendTransitionToEvent(eventName,fromState,toStateName);
        }
    }
    public void setGuardToEvent(String eventName,EventGuard guard)
    {
        Event editedEvent = events.get(eventName);
        editedEvent.setGuard(guard);
    }
 
    //Métodos para a Execução da Máquina
    public boolean fireEvent(String name) throws UndefinedTransitionException
    {
        boolean fire = true;
        Event event = events.get(name);
        if(event != null) 
        {
            if (event.getGuard() != null) 
            {
                if (event.getGuard().shouldPerform() == false) 
                {
                    fire = false;
                }
            }
 
            if (event.existTransition(currentState) == false)
            {
                fire = false;
                throw new UndefinedTransitionException();
            }
 
            if (fire == true)
            {
                if (event.getToState(currentState) != null) 
                {
                    previousState = currentState;
                    currentState = event.getToState(currentState);
                    for (Observer obs : observers)
                    {
                        obs.update(this,getCurrentStateName());
                    }
                    if (event.command != null) 
                    {
                        event.command.execute(previousState.getName());
                    }
                    if (currentState.command != null) 
                    {
                        currentState.command.execute();
                    }
                }
                else 
                {
                    throw new UndefinedStateError();
                }
            }
        }
        else
        {
            throw new UndefinedEventError();
        }
        return fire;
    }
 
    public String getCurrentStateName()
    {
        return this.currentState.getName();
    }
}

Classe Event

package stateMachine;
 
import java.util.*;
 
class Event
{
    public Vector<Transition> transitions = new Vector<Transition>();
    public String name;
    public EventCommand command;
    public EventGuard guard = null;
    public StateMachine machine;
 
    Event(String name,StateMachine machine)
    {
        this.name = name; 
        this.machine = machine;
    }
 
    public void setEventCommand(EventCommand command) 
    {
        this.command = command;
    }
 
    public void setGuard(EventGuard guard) 
    {
        this.guard = guard;
    }
 
    public EventGuard getGuard()
    {
        return this.guard;
    }
 
    public boolean existTransition(State fromState)
    {
        boolean exist = false;
        for (Transition transition : transitions) 
        {
            if(transition.getFromState() == fromState) 
            {
                exist = true;
            }
        }
        return exist;
    }
 
    public State getToState(State fromState)
    {
        State toState = null;
        for(Transition transition : transitions) 
        {
            if(transition.getFromState() == fromState)
            {
                toState = transition.getToState();
            }
        }
        return toState;
    }
}

A visualização do código inteiro pode ser analisada através do download do projeto que está aqui disponível, aqui quero destacar pontos que considero importantes e que foram vistos nesse laboratório.

Uso da classe Dictionary (Hashtable)

Trata-se de uma coleção de elementos identificados por uma chave e que armazena um valor por chave. Seu uso me foi apresentado por um colega e tal ajudou muito o desenvolvimento do laboratório pois seria extremamente cansativo ficar buscando toda hora um vetor para achar o objeto desejado, afinal, a classe vetor era a única que eu conhecia para armazenar objetos. Foram usados dois dicionários, ambos na classe StateMachine, um para armazenar os eventos e o outro para os estados.

Uso de vetor de Transitions em Event

Várias foram as formas que minha mente pensou para se armazenar as transições, mas a escolhida foi a criação de uma classe Transition juntamente com a definição de um vetor de Transitions na classe Event. Tal forma, acredito, foi muito bem vinda uma vez que ela proporcionou um código de fácil entendimento e intuitivo de certa forma. Pensei em usar um dicionário para armazenar as transições em Event, porém essa solução se mostrou insatisfatória ou mais complicada uma vez que havia casos que eu precisava usar a transição para obter seus estados e não seus estados para achar a transição, como ocorre no caso do dicionário.

Uso de classes anônimas e padrões de projeto

O uso de classes anônimas foi feito sem problemas porém achei difícil vislumbrar sua utilização em labs e em projetos quaisquer. Digo, acredito que se precise de boa experiência para que se possa usá-las de maneira sensata e gerando código de alto-nível. O padrão Observer foi novamente utilizado e acredito que suas intensões e implementação estão bem sedimentados. Já o padrão Command foi um bom ganho para a experiência de cada um que ainda não o conhecia, uma vez que sua utilização acrescenta idéias interessantes.

Aplicação desenvolvida

A aplicação desenvolvida para testar a máquina simula as relações geopolíticas entre os EUA e um país árabe. Dependendo da atitude do país árabe, o nível de alerta dos Estados Unidos aumenta ou diminui, até o ponto em que as atitudes do país árabe passam dos limites e os EUA declaram guerra contra o mesmo. Uma vez em guerra, os Estados Unidos não mudam de estado até que o país árabe se renda à seus interesses capitalistas. Os estados iniciais do país árabe e dos EUA são respectivamente: "obedecendo" e "pacifico".

As atitudes que podem ser tomadas pelo país árabe são as seguintes:
- Ofender os EUA publicamente
- Testar armas nucleares
- Quebrar acordos e tratados internacionais
- Abaixar o preço do petróleo
- Obedecer às pressões externas
- Render-se às pressões capitalistas
- Render-se aos ataques capitalistas bombásticos (Ocorre quando os países estão em guerra)

Cada uma dessas atitudes tem transições para todas as outras

Os estados que os Estados Unidos podem se encontrar são os seguintes:
- Pacífico
- Cauteloso
- Alerta Total
- Em Guerra

A figura abaixo ilustra as transições possíveis entre os estados dos Estados Unidos:

fig3.JPG

Fig. 3 - Transições entre os estados de EUA.

O diagrama de classes segue abaixo:

fig4.JPG

Fig. 4 - Diagrama de classes da aplicação desenvolvida

Em seguida, há o resultado da seguinte situação: O país árabe realizou nessa ordem as seguintes ações: ofendeu publicamente os EUA, abaixou o preço do petróleo, quebrou acordos, fez testes nucleares e quebrou acordos novamente. Depois, quis obedecer as pressões externas várias vezes (quando em guerra, nada ocorre a não ser que o país árabe renda-se), mas foi obrigado a render-se para poder terminar com a guerra. Por fim, ele faz mais alguns testes nucleares para deixar a cautela no ar.

Resultado
Meu estado agora é cauteloso
As coisas não vão bem para meus interesses!
Meu estado agora é pacifico
As coisas vão bem para meus interesses!
A paz reina!
Meu estado agora é cauteloso
As coisas não vão bem para meus interesses!
Meu estado agora é alerta_total
As coisas não vão bem para meus interesses!
Meu estado agora é em_guerra
Agora é GUERRA! Acabou a brincadeira!
Para acabar com a guerra, vocês precisam render-se ao capitalismo selvagem!
Meu estado agora é pacifico
A paz reina!
Meu estado agora é cauteloso
As coisas não vão bem para meus interesses!

O código completo das classes que compõem a aplicação também se encontra anexado.

Conclusão

O laboratório em si não era difícil mas acabei me enrolando na implementação. Creio que por minha causa mesmo, desatenção e afins, pois a explicação do laboratório foi muito boa, bem completa e elucidante. Demorei bastante tempo para perceber que eu estava criando diferentes eventos com o mesmo nome, quando na verdade deveria atualizar o já existente. Assim, demorei mais tempo que esperava. Defini algumas variáveis públicas e assim acabei indo contra o paradigma de OO ao manipular variáveis de outras classes diretamente. Isso foi feito por relativa preguiça de criar vários setters e também, e principalmente, porque ao fazer isso a visualização do código foi mais fácil o que ajudou muito na compreensão e na correção de erros (que foram muitos).

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