Pergunta

i feito uma máquina de estado e gostaria de tirar proveito de genéricos em java. Atualmente eu não vejo a maneira que eu posso fazer este trabalho e obter muito procurando código. im certo de que este problema de projeto foi abordado muitas vezes antes, e estou procurando alguma entrada. heres um esboço.

class State { ... }

apenas uma cópia de cada objeto de estado distinto (aulas na sua maioria anónimos ligados a variáveis ??static final), tem dados personalizado para cada estado. cada objecto de estado tem um pai estado (não é um estado raiz)

class Message { ... } 

cada mensagem é criado separadamente e cada um tem dados personalizados. eles podem subclasse outro. há uma classe de mensagem raiz.

class Handler { ... } 

cada manipulador é criado apenas uma vez e lida com um determinado estado / mensagem de combinação.

class StateMachine { ... }

atualmente mantém o controle do estado atual, e uma lista de todos (State, Message) -> mapeamentos Handler. ele tem outras funcionalidades bem. Eu estou tentando manter esta classe genérica e subclasse-lo com parâmetros de tipo como seu usado um monte de vezes no meu programa, e cada vez com um conjunto diferente de Message do / State de / e Handler de. diferentes do StateMachine terá diferentes parâmetros para seus manipuladores.

Aproxime-A

tem estado pista máquina sustento de todos os mapeamentos.

class StateMachine<MH extends MessageHandler> {
  static class Delivery {
    final State state;
    final Class<? extends Message> msg;
  }
  HashMap<Delivery, MH> delegateTable;
  ...
}

class ServerStateMachine extends StateMachine<ServerMessageHandler> {
  ...
}

me permite ter métodos costume manipulador para este máquina de estado particular. os parâmetros para o método handler.process pode ser substituído. No entanto, o condutor não pode ser parametrizado pelo tipo de mensagem.

Problema:. isso envolve o uso da verificação de sanidade instanceof para cada manipulador de mensagem (certificando-se de que está recebendo a mensagem que ele está esperando)

Abordagem B

vamos fazer cada manipulador de mensagem parametrizado pelo tipo de mensagem

class MessageHandler<M extends Message> {
  void process(M msg) { .... }
}

Problema: Tipo de apagamento me impedirá de armazenar estes em um hashmap bom desde que todos os de MessageHandler será digitado de forma diferente. se eu posso armazená-los em um mapa, eu não vou ser capaz de recuperá-los e chamá-los com os argumentos apropriados.

Aproxime-C

tem a alça objeto de estado todas as mensagens.

class State<M extends Message> { ... }
class ServerState<M extends ServerMessage> extends State<M> { ... }

i ter manipuladores de mensagens ligadas a estados específicos de máquina de estado (colocando-os dentro), (cada instância de uma máquina de estado teria a sua própria lista de estados válidos), que permite que os manipuladores de ser de um tipo específico. (Máquina de estado do servidor -> mensagem do servidor manipulador).

Problema: cada estado pode apenas lidar com um tipo de mensagem. você também perde a idéia de que o estado pai pode lidar com mensagens diferentes do que estados criança. Tipo de apagamento também impede a StateMachine de chamar estados atuais processar métodos.

Aproxime-D

tem-se o processo da mensagem com base no estado.

Problema: nunca realmente considerado, uma vez que cada mensagem deve ter um manipulador diferente com base no estado máquina de estado atual. o remetente não vai saber o estado do StateMachine atual.

Abordagem E

esquecer genéricos e estado código rígido / tratamento de mensagens com uma instrução switch.

Problema: sanidade

inseguro Solução:

Obrigado pelo seu todo mundo entrada, eu acho que o problema era que eu não reduziu isto a bom problema (muito discussão) Aqui está o que eu tenho agora.

public class State { }

public class Message { }

public class MessageHandler<T extends Message> { }

public class Delivery<T extends Message> {
  final State state;
  final Class<T> msgClass;
}

public class Container {

  HashMap<Delivery<? extends Message>, MessageHandler<? extends Message>> table;

  public <T extends Message> add(State state, Class<T> msgClass, MessageHandler<T> handler) {
    table.put(new Delivery<T>(state, msgClass), handler);
  }

  public <T extends Message> MessageHandler<T> get(State state, T msg) {
    // UNSAFE - i cannot cast this properly, but the hashmap should be good
    MessageHandler<T> handler = (MessageHandler<T>)table.get(new Delivery<T>(state, msg.getClass()));
    return handler;
  }

}
Foi útil?

Solução

Para abordagem B, não use um "bom" hashmap. Em vez disso, escrever uma typesafe heterogêneo manipuladores de mapeamento recipiente para objetos Classe:

interface Handler<T extends Message> {
...}


interface Message {...}

interface HandlerContainer {

    <T extends Message> void register(Class<T> clazz, Handler<T> handler);

    <T extends Message> Handler<T> getHandler(T t);

}


class HandlerContainerImpl implements HandlerContainer {

    private final Map<Class<?>,Handler<?>> handlers = new HashMap<Class<?>,Handler<?>>();

    <T extends Message> void register(Class<T> clazz, Handler<T> handler) {
          if (clazz==null || handler==null) {
             throw new IllegalArgumentException();
          }
          handlers.put(clazz,handler);
    }

    //Type safety is assured by the register message and generic bounds
    @SuppressWarnings("unchecked")
    <T extends Message> Handler<T> getHandler(T t) {
            return  (Handler<T>)handlers.get(t.getClass());

    }

}

Outras dicas

E com enums representando estados e mensagens é provavelmente o mais fácil. Mas não é muito extensível.

C usando o padrão Visitor nas classes estaduais para expedição no tipo de mensagem parece que ele pode ser o melhor das suas opções. Dada a escolha entre instanceof e Visitor, acho Visitor é um pouco mais limpo (embora ainda estranho). Os problemas apagamento tipo fazer representar uma dificuldade notável, e processamento na mensagem parece um pouco para trás. notação típica máquina de estado tem os estados como o centro de controle. Além disso, você pode ter a classe abstrata Visitor para os tipos de mensagens lançar um erro em todos os estados, permitindo estados para obter erro queda-back on mensagens inválidas gratuitamente.

C + Visitor seria bastante análoga à abordagem que eu freqüentemente uso na implementação de máquinas de estado em C ou idiomas com funções de primeira classe - "estado" é representado por um ponteiro para a função de manipulação de mensagens no estado atual, com que a função retornando um ponteiro para a próxima função de estado (possivelmente próprio). A malha de controle de máquina de estado meramente busca a próxima mensagem, passa para a função de estado atual, e atualiza a noção de "atual" quando ele retorna.

Abordagem E. Esqueça os genéricos e interfaces de uso.

class Message { ... }
class State { ... }

class Machine {
  static State handle(State current, Message msg) {
    ...
  }
}

class CustomMessage extends Message { ... }
class CustomState extends State { ... }

class CustomMachine {
  static CustomState handle(CustomState current, CustomMessage msg) {
    // custom cases
    ...

    // default: generic case
    return Machine.handle(current, msg);
  }
}

A abordagem que eu tenho visto em alguns lugares, é a utilização de anotações. Use classes POJO regulares e anotá-los para serem processados ??por uma classe tipo de gerente que administra a máquina de estado:

public class MyState {
 @OnEntry
 public void startStuff() {
  ...
 }

 @OnExit() 
 public void cleanup() {
  ..
 } 
}

Existem algumas implementações mais desenvolvidos, eu acho que o Scientific Toolbox era bom, mas eu não posso encontrar o link agora: http://mina.apache.org/introduction-to-mina-statemachine. html http://weblogs.java.net/blog/carcassi/ Arquivo / 2007/02 / finite_state_ma_1.html http://hubris.ucsd.edu/shared/manual.pdf

Abordagem F:

Esqueça os genéricos, a menos que você tem padrões específicos do tipo. Definir um par de interfaces por seu sistema desejado, incluindo, talvez, algo como

interface StateMachineState<R extends StateMachineState,T> {
    /* returns next state */
    R execute(T otherState); 
}

e para uma máquina de estado particular, uso enums que se estendem StateMachineState:

class OtherState {
    public double x1;
    public int i;
}

enum MyState extends StateMachineState<MyState,OtherState>
{
    FOO {
       MyState execute(OtherState otherState) { 
           otherState.x1 += 3.0;
           otherState.i++;
           return BAR;
       }
    },
    BAR {
       MyState execute(OtherState otherState) { 
           otherState.x1 -= 1.0;
           otherState.i--;
           return (i % 3 == 0) ? FOO : BAR;
       }
    },         
}

Em seguida, você pode fazer algo como:

MyState state = MyState.FOO;
OtherState otherState = new OtherState();
otherState.i = 77;
otherState.x1 = 3.14159;
while (true)
{
    state = state.execute(otherState);
    /* do something else here */        
}

(ressalva: código não verificado duas vezes por erros de sintaxe)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top