我正在开发一个应用程序,它接收一个原始的二进制消息(非常简单,第一个字节是消息类型,其余是有效负载),然后对它做一些事情。我试图完成的事情是确保网络服务从应用程序的其余部分抽象出来,以便不时地修改协议,而不会对应用程序的其余部分造成太大影响。这个应用程序的上下文是一个非常简单的客户端-服务器游戏,我现在正在为它做客户端工作。
不过,我现在有点挣扎。我需要找到一种优雅的方式,将一个连接抛到某种转换器/适配器服务中,它会返回漂亮的对象(我想)。这些对象将被抛入队列,等待应用程序的其余部分使用。我面临的问题或多或少是这个结构(伪代码):
让我们假设每条消息都是20个字节,所以我可以为每20个字节调用这个函数:
public Message GetMessage(byte[] buffer)
{
switch(buffer[0])
{
case 1:
return Message1(...);
case 2:
return Message2(...);
.....
case n:
return MessageN(...);
}
}显然,我将使用枚举或常量来处理这种情况,但这并不是困扰我的问题。事情是这样的。我想我有大约50个消息类型,这意味着我将得到一个有50个案例的switch语句。我真的想不出一个合适的方法来把它分割成更小的部分,这将导致一个巨大的,容易出错的方法。我想知道是否有任何模式可以让这件事变得更容易,因为我找不到任何模式。
感谢您的提前输入!
发布于 2009-06-20 03:47:46
好吧,当然有很多种方法。标准方法是将函数存储在字典中。在函数式语言中,您可以编写如下内容
import MyProtocol
handler = { mListFirmware : listFirmwareVersions,
mLoadFirmware : startLoadingFirmwareVersion,
mLoadFirmwareBl: loadFirmwareVersionBlock,
...
}
...
try {
handler[message[0]](message[1:])
} catch (NotInTheDictionary e) {
# complain ...
}我不确定你的C/C++/C#是什么版本。如果你不能把函数放在那里,那就把指针放到函数上。如果你的一些函数非常小,在一些语言中,你可以直接使用lambda
...
mLoadFirmware : (lambda (m): start_load(m[1:3]); do_threads()),
...我会做更多的优化。看,每条消息都有一个常量和一个函数名。不过,你不需要重复:
Messages = new Array()
def listFirmwareVersions(m):
...
Messages.add(Name_Of_This_Method(), This_Method)
# it's possible to get name of current function in Python or C#
... # how to send
send_message(Messages.lookup(listFirmwareVersions), ...)
... # how to receive
try {
Messages[message[0]](message[1:])
...但是如果你想在哲学上是正确的,你可以为处理程序创建单独的类:
class MessageHandler:
static int message_handlers = []
int msg
Array data
void handler
Message(a_handler):
msg = message_handlers.add(this)
handler = a_handler
write(Stream s):
s.write(msg, data)
listFirmwareVersions = new MessageHandler(do_firmware)
startLoadingFirmwareVersion = new MessageHandler(load_firmware)
...
... # how to send
listFirmwareVersions.send(...)
... # how to receive
try {
message_handlers[message[0]](message[1:])
...发布于 2009-06-19 15:33:33
我有一些Java代码可以做到这一点。希望你能很容易地转换成C#。实际上,我有一个messageMap集合:
private final Map<Byte, Class<? extends Message>> messageMap;这是从消息is到其对应的Message类的映射。我为每种不同的消息类型调用一次addMessage:
public void addMessage(int id, Class<? extends Message> messageClass) {
messageMap.put((byte) id, messageClass);
}然后,当消息到达时,我读取网络上的消息ID,在messageMap中查找需要实例化的Message类,然后使用反射创建该类的实例。
Class<? extends Message> messageClass = messageMap.get(id);
Message message = messageClass.newInstance();在这里,newInstance()调用默认构造函数。
我已经在具有不同消息的多个应用程序中使用了这种通用消息处理代码。每一个都有一个很好的,简单的代码块来注册不同的消息,如下所示:
// Messages that we can send to the client.
addOutgoingMessage(0, HeartbeatMessage.class);
addOutgoingMessage(1, BeginMessage .class);
addOutgoingMessage(2, CancelMessage .class);
// Messages that the client can send.
addIgnoredMessage (0, HeartbeatMessage.class);
addIncomingMessage(1, StatusMessage .class, statusMessageHandler);
addIncomingMessage(2, ProgressMessage .class, progressMessageHandler);
addIncomingMessage(3, OutputMessage .class, outputMessageHandler);
addIncomingMessage(4, FinishedMessage .class, finishedMessageHandler);
addIncomingMessage(5, CancelledMessage.class, cancelledMessageHandler);
addIncomingMessage(6, ErrorMessage .class, errorMessageHandler);发布于 2009-06-19 15:32:10
您可以有一个由50个函数指针(即C#委托)组成的数组,您可以使用第一个字节的值对其进行索引,也可以有一个委托字典,其关键字是第一个字节的值。不过,这只是编写switch语句的另一种方式。
但它更加分布式:例如,当您创建新的消息类型(可能是新的源文件中的新类)时,您可以调用现有的方法将新的委托添加到委托的静态集合中,而不是编辑源代码来将新的case添加到大的switch语句中。
https://stackoverflow.com/questions/1018564
复制相似问题