首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用状态模式设计在Java中实现通信协议

用状态模式设计在Java中实现通信协议
EN

Stack Overflow用户
提问于 2012-07-03 19:13:15
回答 2查看 4.4K关注 0票数 2

如果在其他地方回答了这个问题,很抱歉;找不到足够的信息来说服我自己做这件事的最佳方法。我还意识到这是一个冗长的解释,没有代码,但是让我知道我是否应该编写一些示例代码来帮助演示我正在做的事情。

基本上:

  • 用System.in在Java中实现通信协议
  • 当前的方法是实现一种状态模式,其中在上下文类中在System.in上实例化扫描程序。
  • 具体状态调用上下文方法从扫描仪读取,然后根据扫描仪返回的值适当地执行操作/过渡状态。

我使用状态模式的初衷是在解析来自System.in的序列时简化代码(不要问语法问题,这是我必须处理的):

  • 命令NAME=X
  • 标头
  • 标题信息行
  • 内容
  • 命令行内容
  • ENDCONTENTS
  • 欧德曼德

我通常为我期望接收的每种类型的命令定义一个具体的状态。以上面的序列为例,我将有一个类似于{WAITING_FOR_COMMAND、COMMAND_RECEIVED、PARSING_HEADER、PARSING_CONTENTS、PARSING_DONE、COMMAND_PROCESSED}的状态集。我最初在WAITING_FOR_COMMAND中,然后当收到“NAME=X命令”时,我会转换到COMMAND_RECEIVED,然后当"HEADER“出现时,我会转换到PARSING_HEADER等等。这种设计使得遍历协议中的所有边缘情况变得更容易,并且使代码更容易更新/维护,以便在协议被调整时使用。显然比大规模的切换语句和重复的边界检查要好得多。

我遇到的问题是,我发现自己在上下文类中声明了越来越多的状态变量,因为我充实了自己的具体状态行为,并且知道这可能很糟糕,因为我正在创建非常公开的接口,并且在上下文和具体状态类之间建立非常高的联系。该协议中的命令序列可以任意长,我需要保存命令序列中每个项所传递的信息,直到命令序列完成为止。

以上面的命令序列为例,在“命令ID=X”之后,我希望在收到"ENDCOMMAND“之后保存值X供以后使用,并完全处理该命令。在" header“之后,我希望在收到"ENDCOMMAND”之后保存标题信息,以便在实际处理命令时使用。就这样等等。简单地将commandId和头状态变量添加到上下文类中就可以了,但在我看来,这一点都不干净,也没有很好的封装。

对于如何处理这个问题,有人有什么高层次的建议吗?有更好的使用状态设计模式吗?

我只是想指出一些我一直在玩的点子:

  • 为每种类型的命令序列定义状态上下文,并在接收到来自System.in的相关命令时调用适当的上下文;这似乎与拥有巨大的开关块一样混乱,而且似乎过于复杂。
  • 设计一个支持复合FSM的完整FSM体系结构,其中每个命令序列都在一个总体FSM中占用自己的FSM;在我看来,这似乎是过火了。
  • 为每种命令序列类型创建一个带有各种子类的ProtocolCommand对象;在转换时,我可以将这些子类传递到每个状态,并在go...but时逐步构建它们,这会使状态接口混乱,并迫使所有状态都吸收它们不一定使用的参数。

非常感谢!对不起,这件事太冗长了,如果我能澄清什么,请告诉我。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-07-03 19:16:58

对于这种解析方式,我使用了一个枚举,每个状态都使用了一个枚举。这里就是一个例子,http://vanillajava.blogspot.co.uk/2011/06/java-secret-using-enum-as-state-machine.html

代码语言:javascript
复制
interface Context {
    ByteBuffer buffer();
    State state();
    void state(State state);
}
interface State {
    /**
       * @return true to keep processing, false to read more data.
     */
    boolean process(Context context);
}
enum States implements State {
    XML {
        public boolean process(Context context) {
            if (context.buffer().remaining() < 16) return false;
            // read header
            if(headerComplete)
                context.state(States.ROOT);
            return true;
        }
    }, ROOT {
        public boolean process(Context context) {
            if (context.buffer().remaining() < 8) return false;
            // read root tag
            if(rootComplete)
                context.state(States.IN_ROOT);
            return true;
        }
    }
}

public void process(Context context) {
    socket.read(context.buffer());
    while(context.state().process(context));
}
票数 1
EN

Stack Overflow用户

发布于 2012-07-03 20:06:05

我首先要考虑以下几点:

  1. 您的状态组件(WAITING_FOR_COMMAND、COMMAND_RECEIVED等)是否是应用程序逻辑的核心?也就是说,真的很重要吗----解析器处于什么状态,它转换到什么状态--应用程序才能正常工作?每个州要持有的值是什么?这些值在不同的状态之间有很大的不同吗?如果您的回答是肯定的,那么可能您有一个有效的国家pattern.But的理由,我经常看到状态机使用的地方,它只是一个简单的过火。
  2. 您的用例似乎更需要命令模式和解释器模式(就像您正在编写语法一样)。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11317603

复制
相关文章

相似问题

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