引言:从收到一个 Message* 开始
在上一篇文章中,我们解决了 Codec(编解码) 的问题。现在,我们的网络服务器已经能够成功地从 TCP 字节流中切分出一个完整的网络包,并利用 Protobuf 的反射机制,将二进制数据还原成了一个 C++ 对象。
但是,这里有一个棘手的问题:由于 C++ 是强类型语言,底层网络库为了通用性,通常只返给我们一个 基类指针 (google::protobuf::Message*)。
当我们拿到这个基类指针时,我们不仅要问:它是谁? 还要问:我该把它交给谁处理?
- 如果是
LoginRequest,我需要调用onLogin()。 - 如果是
SensorData,我需要调用onSensorData()。
面对几十甚至上百种消息类型,我们该如何设计这个路由模块?
第一阶段:噩梦般的 Switch-Case 地狱
最直观(也是最糟糕)的做法,就是利用 if-else 或 switch 进行暴力判断。我们称之为'反面教材'。
// 反面教材:不可维护的代码
void OnMessage(google::protobuf::Message* msg) {
// 1. 获取消息的名字 (反射)
const std::string& type_name = msg->GetDescriptor()->full_name();
// 2. 暴力匹配
if (type_name == "app.LoginRequest") {
// 痛苦的转型
auto specific_msg = dynamic_cast<app::LoginRequest*>(msg);
OnLogin(specific_msg);
} else if (type_name == "app.SensorData") {
auto specific_msg = dynamic_cast<app::SensorData*>(msg);
OnSensorData(specific_msg);
} else if (type_name == "app.LogoutRequest") {
// ...
}
// ... 如果你有 100 种消息,这里就要写 100 个 else if
}
这种写法的致命缺陷:

