首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >java泛型设计问题(状态机)

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

Stack Overflow用户
提问于 2009-08-19 16:09:48
回答 5查看 3K关注 0票数 17

我做了一个状态机,希望它能利用java中的泛型。目前,我没有看到我可以让这个工作和获得漂亮的代码的方式。我相信这个设计问题之前已经被处理过很多次了,我正在寻找一些输入。这里有一个粗略的轮廓。

代码语言:javascript
复制
class State { ... }

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

代码语言:javascript
复制
class Message { ... } 

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

代码语言:javascript
复制
class Handler { ... } 

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

代码语言:javascript
复制
class StateMachine { ... }

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

方法A

让状态机跟踪所有映射。

代码语言:javascript
复制
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

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

代码语言:javascript
复制
class MessageHandler<M extends Message> {
  void process(M msg) { .... }
}

问题:类型擦除将阻止我将它们存储在一个很好的哈希图中,因为所有的MessageHandler的类型都将是不同的。如果我可以将它们存储在一个映射中,我将无法检索它们并使用适当的论据调用它们。

方法C

让state对象处理所有消息。

代码语言:javascript
复制
class State<M extends Message> { ... }
class ServerState<M extends ServerMessage> extends State<M> { ... }

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

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

方法D

让消息的处理本身基于状态。

问题:从未真正考虑过,因为根据当前的状态机状态,每条消息都应该有不同的处理程序。发送者将不知道当前StateMachine的状态。

方法E

忘记泛型和使用switch语句的硬编码状态/消息处理。

问题:健全性

不安全的解决方案:

谢谢大家的意见,我认为问题是我没有把这个问题简化成好问题(太多的讨论)这就是我现在所拥有的。

代码语言:javascript
复制
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;
  }

}
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2009-08-20 07:16:10

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

代码语言:javascript
复制
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());

    }

}
票数 1
EN

Stack Overflow用户

发布于 2009-08-19 16:21:29

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

代码语言:javascript
复制
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);
  }
}
票数 2
EN

Stack Overflow用户

发布于 2009-08-19 16:25:12

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

C在状态类中使用Visitor模式来分派消息类型看起来可能是最好的选择。如果要在instanceof和Visitor之间进行选择,我认为Visitor会稍微干净一些(尽管仍然很尴尬)。类型擦除问题确实构成了一个值得注意的困难,消息中的处理似乎有些倒退。典型的状态机表示法将状态作为控制的中心。此外,您可以让消息类型的Visitor抽象类在所有状态上抛出错误,从而允许状态免费获得无效消息的错误回退。

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1301007

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档