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

我做了一个状态机,并希望它利用java中的泛型。目前,我没有看到如何使这项工作并得到漂亮的代码。我确定这个设计问题以前已经解决过很多次了,我正在寻找一些输入。这是一个粗略的轮廓。

class State { ... }

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

class Message { ... } 

每条消息都是单独创建的,并且每条消息都有自定义数据。它们可以相互细分。有一个根邮件类。

class Handler { ... } 

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

class StateMachine { ... }

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

方法 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) { .... }
}

问题:类型擦除将阻止我将这些存储在一个漂亮的哈希映射中,因为所有's的类型都将以不同的方式输入。如果我可以将它们存储在地图上,我将无法回复它们并使用适当的参数调用它们。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;
  }

}

答案 1

带有表示状态和消息的枚举的 E 可能是最简单的。但它的可扩展性不是很强。

C 在状态类中使用 Visitor 模式来调度消息类型,看起来它可能是最好的选择。如果在访客和访客之间做出选择,我认为访客稍微干净一些(尽管仍然很尴尬)。类型擦除问题确实带来了明显的困难,并且消息中的处理似乎有些倒退。典型的状态机表示法以状态为控制中心。此外,您可以让消息类型的 Visitor 抽象类在所有状态上引发错误,从而允许状态免费获得无效消息的错误回退。instanceof

C+Visitor 与我在用 C 语言或具有一等函数的语言实现状态机时经常使用的方法非常相似 - “state”由指向处理当前状态消息的函数的指针表示,该函数返回指向下一个状态函数(可能是其本身)的指针。状态机控制循环仅获取下一条消息,将其传递给当前状态函数,并在返回时更新“当前”的概念。


答案 2

方法 E. 忘记泛型,使用接口。

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

推荐