首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在c++中透明地处理不同的协议版本?

如何在c++中透明地处理不同的协议版本?
EN

Stack Overflow用户
提问于 2009-09-29 16:07:43
回答 6查看 3.9K关注 0票数 7

这是一个通用的C++设计问题。

我正在编写一个使用客户机/服务器模型的应用程序。现在我在写服务器端。许多客户已经存在(有些是我写的,另一些是第三方写的)。问题是,这些现有的客户端都使用不同的协议版本(这些年来有2-3个协议更改)。

因为我正在重写服务器,所以我认为现在是设计代码的好时机,这样我就可以透明地处理许多不同的协议版本。在所有协议版本中,来自客户端的第一个通信包含协议版本,因此对于每个客户端连接,服务器都确切地知道它需要讨论哪个协议。

这样做的天真方法是在代码中乱扔如下语句:

代码语言:javascript
复制
if (clientProtocolVersion == 1)
    // do something here
else if (clientProtocolVersion == 2)
    // do something else here
else if (clientProtocolVersion == 3)
    // do a third thing here...

这个解决方案很差,原因如下:

  1. 当我添加一个新的协议版本时,我必须在源树中的任何地方发现这些if语句被使用,并修改它们以添加新的功能。
  2. 如果出现了一个新的协议版本,并且协议版本的某些部分与另一个版本相同,我需要修改If语句,以便它们读取if (clientProtoVersion == 5 || clientProtoVersion == 6)
  3. 我相信有更多的原因,为什么它是糟糕的设计,但我现在想不起来。

我要寻找的是一种智能处理不同协议的方法,使用C++语言的特性。我考虑过使用模板类,可能是使用模板参数指定协议版本,或者可能是一个类传家宝,每个不同的协议版本都有一个类.

我相信这是一个非常常见的设计模式,所以很多人之前肯定有过这个问题。

编辑:

你们中的许多人都建议继承遗产,上面是最古老的协议版本,如下(请原谅我的ASCII艺术):

代码语言:javascript
复制
IProtocol
    ^
    |
CProtoVersion1
    ^
    |
CProtoVersion2
    ^
    |
CProtoVersion3

..。就补救而言,这似乎是一件明智的事情。但是,当您需要扩展协议并添加根本新的消息类型时,会发生什么呢?如果我在IProtocol中添加虚拟方法,并在CProtocolVersion4中实现这些新方法,那么这些新方法在早期协议版本中是如何处理的?我想我的选择是:

  • 使默认实现成为NO_OP (或者可能在某个地方记录消息)。
  • 抛出一个异常,尽管这似乎是一个糟糕的主意,即使我正在键入它。
  • ..。做点别的吗?

Edit2:

除了上述问题,当更新的协议消息需要比旧版本更多的输入时,会发生什么?例如:

在protocl版本1中,我可能有:

ByteArray getFooMessage(string param1, int param2)

在协议版本2中,我可能希望:

ByteArray getFooMessage(string param1, int param2, float param3)

这两个不同的协议版本现在都有不同的方法签名,这很好,只不过它迫使我遍历所有调用代码,并根据使用的协议版本将所有的调用都更改为2个params到3个params,这是我首先想要避免的!

:什么是将协议版本信息从代码的其余部分中分离出来的最佳方法,以便对您隐藏当前协议的细节?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2009-09-29 16:21:26

由于您需要动态地选择要使用的协议,因此使用不同的类(而不是模板参数)来选择协议版本似乎是正确的方法。本质上,这是策略模式,虽然访问者也可能是一个可能性,如果你想得到真正的详细。

因为这些都是相同协议的不同版本,所以在基类中可能有共同的东西,然后是子类中的差异。另一种方法可能是让基类用于协议的最老版本,然后让每个后续版本都有一个继承自前一个版本的类。这是一个有点不寻常的继承树,但它的好处是,它保证为以后的版本所做的更改不会影响旧版本。(我假设较早版本的协议的类会很快稳定下来,然后很少改变。

但是,您决定组织层次结构,然后希望在知道协议版本后立即选择协议版本对象,然后将其传递给需要“谈论”协议的各种事情。

票数 10
EN

Stack Overflow用户

发布于 2009-09-29 17:17:01

我也使用过(也听说过)模板来解决这个问题。其思想是将不同的协议分解为基本的原子操作,然后使用类似于增强::融合::矢量的方法从各个块构建协议。

下面是一个非常粗糙的示例(丢失了很多部分):

代码语言:javascript
复制
// These are the kind of atomic operations that we can do:
struct read_string { /* ... */ };
struct write_string { /* ... */ };
struct read_int { /* ... */ };
struct write_int { /* ... */ };

// These are the different protocol versions
typedef vector<read_string, write_int> Protocol1;
typedef vector<read_int, read_string, write_int> Protocol2;
typedef vector<read_int, write_int, read_string, write_int> Protocol3;

// This type *does* the work for a given atomic operation
struct DoAtomicOp {
  void operator ()(read_string & rs) const { ... }
  void operator ()(write_string & ws) const { ... }
  void operator ()(read_int & ri) const { ... }
  void operator ()(write_int & wi) const { ... }
};

template <typename Protocol> void doProtWork ( ... ) {
  Protocl prot;
  for_each (prot, DoAtomicOp (...));
}

因为原型版本是动态的,所以需要一个顶级开关语句来确定要使用哪个原型。

代码语言:javascript
复制
void doWork (int protocol ...) {
  switch (protocol) {
  case PROTOCOL_1:
    doProtWork<Protocol1> (...);
    break;
  case PROTOCOL_2:
    doProtWork<Protocol2> (...);
    break;
  case PROTOCOL_3:
    doProtWork<Protocol3> (...);
    break;
  };
}

要添加新协议(使用现有类型),只需定义原型序列:

代码语言:javascript
复制
typedef vector<read_int, write_int, write_int, write_int> Protocol4;

然后将一个新条目添加到开关语句中。

票数 4
EN

Stack Overflow用户

发布于 2009-09-29 16:20:53

我倾向于使用不同的类来为同一接口的不同协议实现适配器。

根据协议和不同,使用TMP对状态机或协议详细信息可能会有一些好处,但是生成六组使用这六个协议版本的代码可能不值得;运行时多态就足够了,而且在大多数情况下,TCP IO可能会慢到不想硬编码所有内容。

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

https://stackoverflow.com/questions/1493524

复制
相关文章

相似问题

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