`
chelsea
  • 浏览: 111579 次
  • 来自: ...
社区版块
存档分类
最新评论

SAD Pattern: Simple API for Datagram

 
阅读更多

模式名称

  • SAD, Simple API for Datagram

意图

  • 分离网络报文的解析和处理, 使解析代码和处理代码不再耦合在一起, 便于扩展. 类似SAX(Simple API for XML)将XML文档的解析和处理分离到不同的单元中

动机

在网络通信软件的开发中, 经常要处理网络上接收到的各种数据报文. 而收到某种报文后, 需要进行的处理逻辑上可能不止一件事情. 处理过程中会用到报文中的数据, 因此需要对报文进行解析. 而报文的结构通常存在动态部分, 而在C语言中, 无法定义一个数据结构可以直接将报文映射到该结构. 参见<<Navigator Pattern: 导航者模式>>

缺乏考虑的做法通常会把解析和处理放在一起, 一个大函数, 用局部变量甚至全局变量来保存解析出来的数据, 并对其进行各种处理. 这样做的问题是:

  • 难以扩展: 当需要增加新的处理时, 需要在解析过程中多个地方插入处理代码
  • 难以理解: 不同的处理代码混在一起, 和报文解析的逻辑也混在一起, 难以看清楚真正做了什么事
  • 容易出错: 不同的处理共享解析出来的数据, 容易互相影响, 引入错误

另外一种常见的做法是每种不同的处理单独去解析自己需要的内容. 这种方式相对内聚, 但需要解析多遍报文结构, 解析代码也有重复

我们需要更好的设计.

方案

SAX以事件驱动的方式分离了XML文档的解析和处理. 我们可以借鉴. 报文有内部结构, 我们可以使用Navigator模式遍历其内部结构, 并在每一个独立的净荷开始和结束时触发回调, 而对报文内容的各种处理可以以回调函数的形式注册到解析过程中, 为每种处理编写单独的回调函数.

例如, 对于Navigator模式中定义的报文结构, 可以定义如下的API:

typedef void (*MessageHandler)(Message*);
typedef void (*TopLevelPayloadHandler)(TopLevelPayload*);
typedef void (*SecondLevelPayloadHandler)(SecondLevelPayload*);

typedef struct Handler {
    MessageHandler start_handle_message;
    MessageHandler end_handle_message;
    TopLevelPayloadHandler start_handle_toplevel_payload;
    TopLevelPayloadHandler end_handle_toplevel_payload;
    SecondLevelPayloadHandler start_handle_secondlevel_payload;
    SecondLevelPayloadHandler end_handle_secondlevel_payload;
} Handler;

void parse(Message* message, Handler* handlers, int handler_count) {
    for(int i=0; i<handler_count; i++) {
        handlers[i]->start_handle_message(message);
    }
    //遍历Message内部嵌套的payload, 并调用对应的handler, 比如:
    //handlers[i]->start_handle_toplevel_payload(toplevel_payload_pointer);
    //handlers[i]->end_handle_toplevel_payload(toplevel_payload_pointer);

    for(int i=0; i<handler_count; i++) {
        handlers[i]->end_handle_message(message);
    }
}

而每种不同的处理, 只需提供自己的handler即可. 比如可以有打印报文内容的handler, 有根据报文操作硬件的handler, 有持久化报文数据的handler等:

Handler handlers[3] = {
    DataPrinter, 
    HardwareManipulator, 
    DataPersister};

parse(message, handlers, sizeof(handlers)/sizeof(handlers[0]));

效果

  • 报文解析和报文处理的代码彻底分开, 不再纠缠在一起
  • 可以很容易的扩展新的报文处理逻辑
  • 报文只需解析一遍
  • 其约束在于不同的handler之间不应该有依赖

相关模式

  • SAX是处理XML的一种类似的模式, 但其最初的出发点是源于DOM的性能太差, 不过它也有分离解析和处理的效果
  • Visitor模式用于在不改变层次结构的情况下增加对这个层次结构的处理, 并且自动分发正确的处理到正确的节点. 它客观上也分离了数据的解析和对数据的处理.
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics