我制作了一个状态机,并希望它能够利用 Java 中的泛型。目前我没有看到我可以使这项工作并获得漂亮的代码的方式。我确信这个设计问题之前已经被处理过很多次,我正在寻找一些输入。这是一个粗略的轮廓。
class State { ... }
每个不同的状态对象(主要是与静态最终变量绑定的匿名类)只有一个副本,它具有每个状态的自定义数据。每个状态对象都有一个状态父级(有一个根状态)
class Message { ... }
每条消息都是单独创建的,并且每条消息都有自定义数据。它们可以相互子类化。有一个根消息类。
class Handler { ... }
每个处理程序仅创建一次并处理特定的状态/消息组合。
class StateMachine { ... }
当前跟踪当前状态,以及所有 ( State
, Message
) ->Handler
映射的列表。它还具有其他功能。我试图保持这个类的通用性并使用类型参数对其进行子类化,因为它在我的程序中使用了很多次,并且每次都使用一组不同的Message
's / State
's / 和Handler
's。不同StateMachine
的 's 将对其处理程序具有不同的参数。
方法 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> {
...
}
允许我为这个特定的状态机自定义处理程序方法。handler.process 方法的参数可以被覆盖。但是,处理程序不能由消息类型参数化。
问题:这涉及instanceof
对每个消息处理程序使用健全性检查(确保它正在获取它所期望的消息)。
方法 B
让每个消息处理程序按消息类型参数化
class MessageHandler<M extends Message> {
void process(M msg) { .... }
}
问题:类型擦除会阻止我将这些存储在一个不错的哈希图中,因为所有的MessageHandler
' 都会以不同的方式输入。如果我可以将它们存储在地图中,我将无法检索它们并以适当的论点调用它们。
方法 C
让状态对象处理所有消息。
class State<M extends Message> { ... }
class ServerState<M extends ServerMessage> extends State<M> { ... }
我有与特定状态机状态相关的消息处理程序(通过将它们放入内部),(状态机的每个实例都有自己的有效状态列表),这允许处理程序属于特定类型。(服务器状态机 -> 服务器消息处理程序)。
问题:每个状态只能处理一种消息类型。您也失去了父状态可以处理与子状态不同的消息的想法。类型擦除还可以防止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;
}
}