Lab3 Diogo Cassimiro

aluno: Diogo Cassimiro
ano/sem: 2008/segundo semestre
data do laboratório (num. da semana) : 09/10/2008 (2)

Introdução

Neste laboratório foi vista a implementação de uma máquina de estados. Como o objeto principal deste tema considerei o uso de padrões de projeto como o Observer e um pouco do template e strategy, o observer foi claramente utilizado para a vizualização das mudanças de estado na maquina de estados pela entidade que tem o estado. A separação do estado e da entidade em si já pode ser considerada uma característica de padrão strategy, enquanto que a interface de implementação do statemachine e do Observer pode ser considerada uma característica de padrão template. Criamos então uma statemachine que independia do elemento ao qual ela estava atrelada, o que estava de acordo com a boa prática de desacoplamento, assim uma mudanças no código da main nada tinham a ver com mudanças no state machine, e também poderíamos fazer mudanças internas no state machine de forma que o código continuasse funcionando externamente do mesmo modo. fizemos uso de classes anônimas para implementar e extender outras classes de forma a não precisarmos nomear cada uma e assim nos familiarizamos com este novo conceito, utilizamos lançamento de exceptions para controle de erros e eventos não desejados no programa, tornando assim o código mais robusto e menos sujeito a erros.

Desenvolvimento

O desenvolvimento foi simples, visto que dada a interface do statemachine segui de forma a fazer o overload das classes uma a uma, e desta forma no final o a máquina de estados estava pronta. É claro, a medida que foram sendo implementadas as funções da statemachine e tendo uma idéia das entidades envolvidas foram criadas novas classes para realizar a implementação de forma a manter um nível de desacoplamento razoável distribuindo funcionalidades entre as classes tornando elas mais específicas, portanto mais reconhecíveis e melhorando o encapsulamento, para isto a mentalidade usada foi basicamente a do padrão de projeto strategy fora os padrões de projeto já incluídos no esqueleto do código fornecido nas instruções. Dessa forma procurou-se fazer composições através de novas classes que encapsulavam partes que não era interessante que outras partes se preocupassem com a implementação, melhorando o desacomplamento e evitando repetição de código, algumas repetições de pequena escala foram ignoradas pois não compensavam a montagem de classes ou funções pois seu uso não era constante.
Na mentalidade acima apresentada criamos a classe Event, que armazenaria o nome do evento, as transições de estado possíveis para este evento, a condição para ser realizado este evento e a ação que acontece quando este evento ocorre. Nessa mesma linha de pensamento foi criada a classe State que armazena o estado e o comando a ser realizado quando se entra neste estado.

package stateMachine;
 
/**
 *
 * @author Diogo
 */
class State 
{
    private String name;
    private StateCommand command;
    public State(String name, StateCommand command) 
    {
        this.name = name;
        this.command = command;
    }
 
    public State(String name,StateMachine sm) 
    {
        this.name = name;
        this.command = new StateCommand (sm){
            public void execute(){
            //esse metodo nao faz nada
                ;
            }
        } ;
    }
 
    public String getName()
    {
        return name;
    }
 
    public void execute()
    {
       this.command.execute();        
    }
 
}
package stateMachine;
 
import java.util.Vector;
/**
 *
 * @author Diogo
 */
class Event 
{
 
    Vector<String []> fromToList;
    StateMachine sm;
    String name;
    EventCommand command; 
    EventGuard   guard;   
 
    public Event(String name, String[] from, String to, EventCommand onSuccess,StateMachine sm) 
    {
        this.name = name;
        this.sm = sm;
        this.command=onSuccess;
        fromToList = new Vector<String[]>();
        addFromto(from, to);
        createNullGuard();
 
    }
 
    public Event(String name, String[] from, String to,StateMachine sm) 
    {
        this.name=name;
        this.sm = sm;
        fromToList = new Vector<String[]>();
        addFromto(from, to);
        createNullCommand();
        createNullGuard();
    }
 
    public Event(String name, String from, String to, EventCommand onSuccess,StateMachine sm) 
    {
        this.name=name;
        this.sm = sm;
        this.command= onSuccess;
        fromToList = new Vector<String[]>();
        String [] aux= {from,to};
        fromToList.add(aux);
        createNullGuard();        
    }
 
    public Event(String name, String from, String to,StateMachine sm) 
    {
        this.name=name;
        this.sm = sm;
        fromToList = new Vector<String[]>();
        String [] aux= {from,to};
        fromToList.add(aux);
        createNullCommand();
        createNullGuard();        
    }
 
    public void setEventGuard(EventGuard eventGuard) 
    {
        this.guard = eventGuard;
    }   
 
    public void addFromto(String []from, String to)
    {
        String [] alfa;
        for(String x:from)
        {
            alfa=new String[2];
            alfa[0]=x;
            alfa[1]=to;
 
            fromToList.add(alfa);        
        }        
    }
    public void appendTransition(String []from, String to)
    {
        String [] alfa = {null,null};
        for(String x:from)
        {
            alfa=new String[2];
            alfa[0]=x;
            alfa[1]=to;
            for(String [] y :fromToList)
            {
                 if((y[0].equals(x))&&(y[1].equals(to)))
                {
                    return;
                }
            }
            fromToList.add(alfa);        
        }    
    }
 
    public void appendTransition(String from, String to)
    {
 
            for(String [] x :fromToList)
            {
                //nao vamos deixar redundancias na lista de transicoes
                if(x[0].equals(from)&&x[1].equals(to))
                    return;
            }
            String [] alfa = {null,null};        
            alfa[0]=from;
            alfa[1]=to;
            fromToList.add(alfa);        
    }
    public String getName() 
    {
        return name;
    }
 
    public boolean fire() throws UndefinedTransitionException
    {
        for(String [] x: fromToList)
        {
            if(x[0].equals(sm.getCurrentStateName())&&this.guard.shouldPerform())
            {
                this.command.execute(sm.getCurrentStateName());
                sm.setInitialState(x[1]);
                return true;
            }
 
        }
        throw new UndefinedTransitionException();
    }
    private void createNullGuard()
    {
        this.guard=new EventGuard(){
            public boolean shouldPerform()
            {
              //this method does nothing
              return true;
            }
        };
    }
 
    private void createNullCommand()
    {
        this.command=new EventCommand(sm){
           public void execute(String alfa)
            {
              //this method does nothing
              ;
            }
        };        
    }                    
}

Repare que em alguns momentos criei algumas classes que tinham funções que não faziam nada, preferi fazer isto pois tais classes poderiam ser chamadas por métodos mas nem sempre elas eram passadas no contrutor da classe logo eu criava uma classe para evitar um nullpointerexception ou a necessidade de um condicional para a evitar que isto ocorresse. Desta forma qualquer função poderia chamar uma função existente dessas classes sem que isto gerasse algum problema ou a necessidade de alguma consideração a mais, essas classes foram implementadas de forma a simplesmente não fazerem nada e deixarem o programa continuar. Quanto aos construtoresm realmente não eram necessários tantos contrutores para o event visto que poderíamos usar o SetEvenGuard para implementar as chamadas do statemachine onde se quisesse criar um evento com um eventguard, mas como isto é mais uma questão de preferência e estética não alterei nada. Para administrar as exceções que se queria gerar no código em caso de eventos não desejados apenas criamos classes vazias que extendiam as respectivas exceções.
A máquina de estados em si, como dito foi feita implementado as funções dadas na interface StateMachineLayout, foram adicionados apenas variáveis para armazenar os eventos estados e observadores que estariam perscrutando a máquina de estados, criando uma lista do tipo vector para cada tipo, e mais uma variável para armazenar o estado atual da máquina, fora isso foram criadas algumas funções privadas para facilitar implementação e reduzir repetição de código.

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package stateMachine;
 
import java.util.Vector;
 
/**
 *
 * @author Diogo
 */
public class StateMachine implements StateMachineLayout
{
 
    private Vector<Observer> obsList;
    private Vector<Event> eventList;
    private Vector<State> stateList;
    private State curState;
 
    public StateMachine ()
    {
        obsList = new Vector<Observer>();
        eventList = new Vector<Event>();
        stateList =new  Vector<State>();        
    }        
 
    public void appendTransitionToEvent(String event, String fromState, String toState) 
    {
        getEventbyName(event).appendTransition(fromState, toState);
        return;
    }
 
    public void appendTransitionToEvent(String event, String[] fromState, String toState) 
    {
        getEventbyName(event).appendTransition(fromState, toState);
        return;
    }
 
    public void createEvent(String name, String[] from, String to, EventCommand onSuccess) 
    {
        eventList.add(new Event(name, from, to, onSuccess,this));
        return;
    }
 
    public void createEvent(String name, String[] from, String to) 
    {
          eventList.add(new Event(name, from, to, this));
          return;
    }
 
    public void createEvent(String name, String from, String to, EventCommand onSuccess) 
    {
         eventList.add(new Event(name, from, to, onSuccess,this));
         return;
    }
 
    public void createEvent(String name, String from, String to) 
    {
         eventList.add(new Event(name, from, to,this));
         return;
    }
 
    public void createState(String name, StateCommand onEnter) 
    {
 
        for(State x:stateList)
           if (x.getName().equals(name))
                return;       
 
        stateList.add(new State(name,onEnter));
       return;
    }
 
    public void createState(String name) 
    {
     //nao vamos deixar escrever mais de um estado com o mesmo nome
        for(State x:stateList)
            if (x.getName().equals(name))
                return;           
        stateList.add(new State(name,this));
        return;
    }
 
    public boolean fireEvent(String name) throws UndefinedTransitionException 
    {
        try
        {
            if( getEventbyName(name).fire())
                curState.execute();
                    return true;
        }
        catch(UndefinedTransitionException e)
        {throw new UndefinedTransitionException();}
    }
 
    public void notifyObservers() 
    {
    for(Observer x: obsList)
        x.update(this, curState.getName());
    }
 
    public void registerObserver(Observer observer) 
    {
        //nao vamos deixar haver duas referencias a um mesmo observer na lista 
        //caso contrario no notify observers eu poderia notificar mais de uma
        //vez um observer por causa de uma referencia repetida
        for(Observer x:obsList)
            if(x==observer) 
                return;
        obsList.add(observer);
        return;    
    }
 
    public void setGuardToEvent(String event, EventGuard g) 
    {
        getEventbyName(event).setEventGuard(g);
        return;  
    }
 
    public void setInitialState(String name) 
    {
        curState = getStatebyName(name);
        return;
    }
 
    public String getCurrentStateName() 
    {
        return curState.getName();
    }
 
    private Event getEventbyName(String event)
    {
        for(Event x:eventList)
       {
            if(x.getName().equals(event))
                return x;   
       }      
        throw new UndefinedEventException();
    }
 
    private State getStatebyName(String name)
    {
        for(State x: stateList)
            if(x.getName().equals(name))
                return x;
        throw new UndefinedStateException();
    }    
 }

Implementada a máquina de estados foram feitos os testes automatizados primeiramente para poupar-nos o esforço de testes manuais em caso de erros, que deram todos resultados positivos. Embora mesmo depois de corrigidos erros que deram nos testes, ainda houveram erros nos testes manuais, mas isto nos fez já eliminar de cara uma boa parte dos erros de implementação embora no total não tenham sido muitos, isso nos mostra duas hipóteses: temos a necessidade dos testes manuais para verificar o funcionamento real do programa e/ou a necessidade de testes automatizados mais consistentes para não deixar brechas no programa que satisfaçam os testes mas não aonde queremos chegar.

testesautomaticos

Fizemos então os testes manuais com o auxílio do BlueJ primeiramente para o exemplo dado pelas instruções de laboratório, dando os resultados seguintes, os quais me resumirei a falar que foram satisfatórios, não me delongarei mais pois falarei mais sobre resultado de testes na proxima implementação. testamos algumas mudanças de estado e os efeitos deste na turma e no professor:

bluj1bluj2bluj3

Então partimos para uma implementação própria da máquina de estados para teste do pacote stateMachine, criamos então a situação de uma pessoa que dependendo de suas atitudes atinge a santidade ou a condenaçao percorrendo diversos estados no meio do caminho, por meio de pecados e boas ações, mas esta pessoa sofre influencia de seu demonio e anjo interiores, que exerciam influencia mais forte ou mais fraca a partir
das atitudes do Sinner, que seria a pessoa em questão, desta forma um eventguard é acionado e impede por exemplo que uma ação boa aconteça se a influencia do demonio interior for maior, a influencia do demonio e do anjo é dada pelo número de pecados do sujeito e pela motivação dos próprios que funciona como uma máquina de estados, baseada na fé e no número de pecados do sujeito.

Para criarmos estas duas máquinas de estados interdependentes, criamos uma superclasse abstrata que seria a SpiritualInfluence a qual as classes Angel e Devil extendem, criamos então uma maquina de estados tanto no Sinner como no SpiritualInfluence de forma que estas duas implementassem a interface observer para recebessem as atualizações de status de suas respectivas statemachines. Poderiamos ter registrado na lista de observers do Sinner o Observador SpiritualInfluence, dessa forma poderia determinar o comportamento das entidades influenciadoras a partir do estado do Sinner, mas optei por não fazê-lo.
Criei então quatro classes de eventcommand que definiam basicamente um pecado, uma boa ação, e uma ação que aumentasse mais ainda fé do sujeito e uma má ação que implicava um pecado mas não perda de fé. Então foram criados dois EventGuard um para boas e outro para más ações, que representavam a disputa de influências das entidades na pessoa e dois eventcommands para condições especiais dos estados extremos. Testamos então a implementação e corrigimos erros e detalhes de algoritmo até atingir os resultados desejados, testamos então novamente diversos eventos para verificar o funcionamento dos mesmos.
Infelizmente a máquina de estado se tornou muito grande tanto em estados quanto em eventos, e embora a máquina pudesse ficar ainda melhor ilustrada com ainda mais eventos e estados decidimos que o número colocado seria satisfatório visto que o que se deseja é somente uma aplicação para teste e não uma implementação realmente com aplicabilidade.

bluj4bluj5

Segue o diagrama de sistema modelado ja com a aplicação:

diagrama

Conclusão

Este laboratório foi interessante em diversos aspectos, primeiramente como foi falado na introdução pelos padrões de projetos aplicados, em segundo lugar pela liberdade de criação, embora na verdade não tenha sido tão grande mas foi maior que nos outros laboratórios, mas de qualquer forma, conceder mais liberdade acabaria por tornar o laboratório vago e possivelmente os alunos iriam implmentar coisas diferentes do que os professores esperam. Quanto ao nivel de desacoplamento concedido pelo StateMachine pudemos ver tanto pelo diagrama quanto pelas classes que a aplicação fica completamente independente da implementação do statemachine, assim podemos implementar da forma que quisermos o state machine desde que mantenhamos a interface utilizada. Talvez fosse uma boa idéia que cada implementação da interface pudesse representar um estado, assim poderíamos declarar uma StateMachine do tipo da interface e esta receberia uma implementação da própria dependendo do estado em que se deseja chegar. Deixe-me tentar exemplificar:

StateMachineLayout sm = new NovoEstado

onde NovoEstado implementa StateMachineLayout.

Dessa forma para criarmos novos estados dinamicamente teríamos que criar classes dinamicamente o que se possível não seria simples.

Mais alguns pontos positivos deste laboratório foi o enfoque no padrão observer que pode garantir um menor número de acessos a statemachine se necessário, e nos deixando mais a par do funcionamento do mesmo, embora eu infelizmente tenha tomado plena ciência de seu funcionamento agora após a prova. O uso de classes anônimas que me deu a oportunidade de pensar em algumas artimanhas para seu uso, como por exemplo, evitar de forma sucinta um erro devido a um objeto não inicializado no contrutor criando uma instancia de um objeto de classe anônima.

Realmente este laboratório consumiu muito tempo, mas não pela máquina de estados em si que inclusive considerei que o tempo para sua implementação foi bem razoável, no entanto somado a implementação pedida e o relatório atrelado aos testes das duas implementações além dos comentários do relatório em si tomou bastante tempo.

obs: Os projetos em anexo contem o statemachine e a implementacao teste do statemachine criada por mim e a contida nas instruções do laboratório

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