使用状态模式设计在Java中实现通信协议 [英] Implementing communication protocol in Java using state pattern design

查看:165
本文介绍了使用状态模式设计在Java中实现通信协议的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

道歉,如果这在其他地方回答;找不到足够的信息来说服自己做最好的方法。我也意识到这是一个没有代码的漫长的解释,但让我知道,如果我应该鞭打一些示例代码来帮助展示我在做什么。



基本上:




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


我使用状态模式的原始意图是在从System.in中解析这样的序列时简化代码(不要问关于语法,这是我有的使用):




  • 命令名称= X
  • HEADER
  • 标题信息行
  • 目录
  • 命令内容行
  • ENDCONTENTS
  • ENDCOMMAND


我通常为每种类型的命令定义一个具体的状态。使用上述顺序作为示例,我将有一个状态集,看起来像{WAITING_FOR_COMMAND,COM​​MAND_RECEIVED,PARSING_HEADER,PARSING_CONTENTS,PARSING_DONE,COMMAND_PROCESSED}。我最初会在WAITING_FOR_COMMAND,那么当接收到COMMAND NAME = X时,我将转换到COMMAND_RECEIVED,然后当HEADER进入我将转换到PARSING_HEADER等时。这个设计使得遍历所有的边缘情况该协议更容易,并且还使得当协议被调整时,代码易于更新/维护。显然比大规模的switch语句和重复的边界检查好得多。



我遇到的问题是我发现自己在上下文类中声明越来越多的状态变量,因为我肉体我的具体的国家行为,并知道这可能是坏的,因为我创建非常暴露的界面和上下文和具体的状态类之间的非常高的联系。该协议中的命令序列可以任意长,我需要保存命令序列中每个项目赋予的信息,直到命令序列完成。



使用上述命令序列作为示例,在COMMAND ID = X之后,我希望在收到ENDCOMMAND后保存值X以供将来使用,并完全处理命令。在HEADER之后,当我实际处理命令后,我收到ENDCOMMAND后,我想保存头信息供将来使用。等等等等。简单地将commandId和头状态变量添加到上下文类中,现在可以工作,但是根本看起来好像没有干净或很好的封装。



有没有人有任何高级关于他们如何处理这个问题的建议?有更好的使用国家设计模式吗?



只是为了注意一些我一直在玩的想法:




  • 为每种类型的命令序列定义状态上下文,并在从System.in接收相关命令时调用适当的上下文;这似乎与拥有巨型交换机块一样混乱,似乎过度卷曲设计
  • 设计一个支持复合FSM的完整FSM架构,其中每个命令序列在总体FSM内占据其自己的FSM;这对我来说似乎是过度的
  • 为每个命令序列类型创建一个具有各种子类的ProtocolCommand对象;我可以将它们转换成每个状态,并且逐渐建立起来,但是这种方式会阻塞状态界面,并强制所有状态都会使用


非常感谢!对不起,这是如此冗长,让我知道如果我可以澄清任何东西。

解决方案

我已经使用了一个枚举这种风格的解析每个州有一个枚举。一个例子在这里
http://vanillajava.blogspot.co.uk/2011/06/java-secret-using-enum-as-state-machine.html

  interface Context {
ByteBuffer buffer();
状态();
void state(State state);
}
接口状态{
/ **
* @return true保持处理,错误读取更多数据。
* /
布尔进程(Context context);
}
枚举状态{
XML {
public boolean process(Context context){
if(context.buffer()。remaining()< 16 )返回false;
//读取头
if(headerComplete)
context.state(States.ROOT);
返回true;
}
},ROOT {
public boolean process(Context context){
if(context.buffer()。remaining()< 8)return false;
//读取根标签
if(rootComplete)
context.state(States.IN_ROOT);
返回true;
}
}
}

public void process(Context context){
socket.read(context.buffer());
while(context.state()。process(context));
}


Apologies if this is answered elsewhere; couldn't find enough information to convince myself of the best way to do this. I also realize this is a lengthy explanation with no code, but let me know if I should whip up some sample code to help demonstrate what I'm doing.

Basically:

  • implementing a communication protocol in Java using System.in/out
  • current approach is implementing a state pattern where a Scanner is instantiated on System.in in the context class
  • concrete states invoke a context method to read from the Scanner, and subsequently perform actions/transition states appropriately based on the value returned by the Scanner

My original intent for using a state pattern was to simplify the code when parsing sequences like this from System.in (don't ask about the syntax, it's something I have to work with):

  • COMMAND NAME=X
  • HEADER
  • line of header information
  • CONTENTS
  • lines of command contents
  • ENDCONTENTS
  • ENDCOMMAND

I generally define a concrete state for each type of command I expect to receive. Using the above sequence as an example, I would have a state set that looked something like {WAITING_FOR_COMMAND, COMMAND_RECEIVED, PARSING_HEADER, PARSING_CONTENTS, PARSING_DONE, COMMAND_PROCESSED}. I'd initially be in WAITING_FOR_COMMAND, then when "COMMAND NAME=X" is received I'd transition to COMMAND_RECEIVED, then when "HEADER" came in I'd transition to PARSING_HEADER, etc. This design makes traversing all the edge cases in the protocol easier, and also makes the code easy to update/maintain for when the protocol gets tweaked. Obviously much better than massive switch statements and repetitive boundary checks.

The problem I'm having is that I find myself declaring more and more state variables in the context class as I flesh out my concrete state behaviors, and know this is probably bad since I'm creating very exposed interfaces and very high linkages between the context and concrete state classes. The command sequences in this protocol can be arbitrarily long, and I need to save information imparted by each item in the command sequence until the command sequence is complete.

Using the above command sequence as an example, after "COMMAND ID=X", I want to save the value X for future use after I receive "ENDCOMMAND" and fully process the command. After "HEADER", I want to save the header information for future use after I receive "ENDCOMMAND" for when I actually process the command. So on and so forth. Simply adding commandId and header state variables to the context class works for now, but doesn't seem clean or well encapsulated to me at all.

Does anyone have any high level suggestions on how they'd approach this problem? Is there a better use of a state design pattern for this?

Just to note some ideas I've been playing with:

  • defining state contexts for each type of command sequence, and invoking the appropriate context upon receiving the relevant command from System.in; this seems almost as messy as having giant switch blocks, and seems to overly convolute the design
  • designing a full-blown FSM architecture that supports composite FSMs, where each command sequence occupies its own FSM within an overarching FSM; this seems like overkill to me
  • creating a ProtocolCommand object with various subclasses for each command sequence type; I could pass these into each state when transitioning, and gradually build them up as I go...but this clutters the state interface and forces all states to ingest a parameter that they won't necessarily use

Thanks so much! Sorry this was so verbose, let me know if I can clarify anything.

解决方案

I have used an enum for this style of parsing with one enum for each state. An example is here http://vanillajava.blogspot.co.uk/2011/06/java-secret-using-enum-as-state-machine.html

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

这篇关于使用状态模式设计在Java中实现通信协议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆