java泛型设计问题(状态机)

aep*_*iet 17 java generics design-patterns state-machine

我创建了一个状态机,并希望它利用java中的泛型.目前我没有看到我可以使这项工作的方式,并获得漂亮的代码.我确定此设计问题已经多次接近,我正在寻找一些输入.这是一个粗略的轮廓.

class State { ... }
Run Code Online (Sandbox Code Playgroud)

每个不同的状态对象只有一个副本(大多数是与静态最终变量绑定的匿名类),它具有每个状态的自定义数据.每个状态对象都有一个状态父(有一个根状态)

class Message { ... } 
Run Code Online (Sandbox Code Playgroud)

每条消息都是单独创建的,每条消息都有自定义数据.他们可以互相分类.有一个根消息类.

class Handler { ... } 
Run Code Online (Sandbox Code Playgroud)

每个处理程序只创建一次并处理特定的状态/消息组合.

class StateMachine { ... }
Run Code Online (Sandbox Code Playgroud)

当前跟踪当前状态,以及所有(State,Message) - > Handler映射的列表.它还有其他功能.我试图保持这个类通用,并使用类型参数对其进行子类化,因为它在我的程序中使用了很多次,并且每次使用不同的Message's/State's和Handler's.不同StateMachine的将有不同的参数给他们的处理程序.

方法A.

让状态机跟踪所有映射.

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> {
  ...
}
Run Code Online (Sandbox Code Playgroud)

允许我为这个特定的状态机拥有自定义处理程序方法.可以覆盖handler.process方法的参数.但是,处理程序无法通过消息类型进行参数化.

问题:这涉及instanceof对每个消息处理程序使用健全性检查(确保它获得它期望的消息).

方法B.

让我们按消息类型参数化每个消息处理程序

class MessageHandler<M extends Message> {
  void process(M msg) { .... }
}
Run Code Online (Sandbox Code Playgroud)

问题:类型擦除会阻止我将它们存储在一个漂亮的hashmap中,因为所有MessageHandler的都将以不同的方式输入.如果我可以将它们存储在地图中,我将无法撤回它们并用适当的争论来称呼它们.

方法C.

让状态对象处理所有消息.

class State<M extends Message> { ... }
class ServerState<M extends ServerMessage> extends State<M> { ... }
Run Code Online (Sandbox Code Playgroud)

我有消息处理程序绑定到特定的状态机状态(通过将它们放在里面),(状态机的每个实例都有自己的有效状态列表),这允许处理程序具有特定类型.(服务器状态机 - >服务器消息处理程序).

问题:每个州只能处理一种消息类型.您也失去了父状态可以处理与子状态不同的消息的想法.类型擦除还可以防止StateMachine调用当前状态的进程方法.

方法D.

根据状态自己拥有消息的进程.

问题:从未真正考虑过,因为每个消息应该具有基于当前状态机状态的不同处理程序.发件人不会知道当前StateMachine的状态.

方法E.

忘记使用switch语句进行泛型和硬代码状态/消息处理.

问题: 理智

不安全的解决方案

感谢大家的投入,我认为问题是我没有把这个减少到好问题(太多的讨论)继承人我现在拥有的东西.

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;
  }

}
Run Code Online (Sandbox Code Playgroud)

use*_*242 1

对于方法 B,不要使用“好的”哈希图。相反,编写一个异构类型安全容器将处理程序映射到 Class 对象:

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());

    }

}
Run Code Online (Sandbox Code Playgroud)