这是一个通用的C++设计问题。
我正在编写一个使用客户机/服务器模型的应用程序。现在我在写服务器端。许多客户已经存在(有些是我写的,另一些是第三方写的)。问题是,这些现有的客户端都使用不同的协议版本(这些年来有2-3个协议更改)。
因为我正在重写服务器,所以我认为现在是设计代码的好时机,这样我就可以透明地处理许多不同的协议版本。在所有协议版本中,来自客户端的第一个通信包含协议版本,因此对于每个客户端连接,服务器都确切地知道它需要讨论哪个协议。
这样做的天真方法是在代码中乱扔如下语句:
if (clientProtocolVersion == 1)
// do something here
else if (clientProtocolVersion == 2)
// do something else here
else if (clientProtocolVersion == 3)
// do a third thing here...这个解决方案很差,原因如下:
if (clientProtoVersion == 5 || clientProtoVersion == 6)。我要寻找的是一种智能处理不同协议的方法,使用C++语言的特性。我考虑过使用模板类,可能是使用模板参数指定协议版本,或者可能是一个类传家宝,每个不同的协议版本都有一个类.
我相信这是一个非常常见的设计模式,所以很多人之前肯定有过这个问题。
编辑:
你们中的许多人都建议继承遗产,上面是最古老的协议版本,如下(请原谅我的ASCII艺术):
IProtocol
^
|
CProtoVersion1
^
|
CProtoVersion2
^
|
CProtoVersion3..。就补救而言,这似乎是一件明智的事情。但是,当您需要扩展协议并添加根本新的消息类型时,会发生什么呢?如果我在IProtocol中添加虚拟方法,并在CProtocolVersion4中实现这些新方法,那么这些新方法在早期协议版本中是如何处理的?我想我的选择是:
Edit2:
除了上述问题,当更新的协议消息需要比旧版本更多的输入时,会发生什么?例如:
在protocl版本1中,我可能有:
ByteArray getFooMessage(string param1, int param2)
在协议版本2中,我可能希望:
ByteArray getFooMessage(string param1, int param2, float param3)
这两个不同的协议版本现在都有不同的方法签名,这很好,只不过它迫使我遍历所有调用代码,并根据使用的协议版本将所有的调用都更改为2个params到3个params,这是我首先想要避免的!
:什么是将协议版本信息从代码的其余部分中分离出来的最佳方法,以便对您隐藏当前协议的细节?
发布于 2009-09-29 16:21:26
由于您需要动态地选择要使用的协议,因此使用不同的类(而不是模板参数)来选择协议版本似乎是正确的方法。本质上,这是策略模式,虽然访问者也可能是一个可能性,如果你想得到真正的详细。
因为这些都是相同协议的不同版本,所以在基类中可能有共同的东西,然后是子类中的差异。另一种方法可能是让基类用于协议的最老版本,然后让每个后续版本都有一个继承自前一个版本的类。这是一个有点不寻常的继承树,但它的好处是,它保证为以后的版本所做的更改不会影响旧版本。(我假设较早版本的协议的类会很快稳定下来,然后很少改变。
但是,您决定组织层次结构,然后希望在知道协议版本后立即选择协议版本对象,然后将其传递给需要“谈论”协议的各种事情。
发布于 2009-09-29 17:17:01
我也使用过(也听说过)模板来解决这个问题。其思想是将不同的协议分解为基本的原子操作,然后使用类似于增强::融合::矢量的方法从各个块构建协议。
下面是一个非常粗糙的示例(丢失了很多部分):
// 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 (...));
}因为原型版本是动态的,所以需要一个顶级开关语句来确定要使用哪个原型。
void doWork (int protocol ...) {
switch (protocol) {
case PROTOCOL_1:
doProtWork<Protocol1> (...);
break;
case PROTOCOL_2:
doProtWork<Protocol2> (...);
break;
case PROTOCOL_3:
doProtWork<Protocol3> (...);
break;
};
}要添加新协议(使用现有类型),只需定义原型序列:
typedef vector<read_int, write_int, write_int, write_int> Protocol4;然后将一个新条目添加到开关语句中。
发布于 2009-09-29 16:20:53
我倾向于使用不同的类来为同一接口的不同协议实现适配器。
根据协议和不同,使用TMP对状态机或协议详细信息可能会有一些好处,但是生成六组使用这六个协议版本的代码可能不值得;运行时多态就足够了,而且在大多数情况下,TCP IO可能会慢到不想硬编码所有内容。
https://stackoverflow.com/questions/1493524
复制相似问题