ChatServiec 负责解耦业务模块和网络模块,其内部保存了各个业务模块的handler
(业务函数)。考虑其ChatService
的唯一性,我们使用单例模式。
ChatService的单例模式
Class ChatService
{
public:
// ChatService 单例模式
// thread safe
static ChatService* instance() {
// C++11保证静态局部对象是线程安全的
static ChatService service;
return &service;
}
/....../
private:
ChatService();
ChatService(const ChatService&) = delete;
ChatService& operator=(const ChatService&) = delete;
};
ChatService()
构造函数被私有化,外界只能调用ChatService
的静态方法。- 类中的
static
变量会保证全局唯一,多个实例共享一个static
变量,如果该static
变量已经初始化过了,不会再次初始化。static
修饰过的成员变量和方法独立于类的任何对象。正是运用此性质,每次只得到之前已经初始化后的static
变量。 - C++ 11 保证 static 成员变量是线程安全的。
ChatService如何解耦合各个模块?
回顾之前 ChatServer 代码,其中的核心就是getHandler()
函数。
void ChatServer::onMessage(const TcpConnectionPtr &conn,
Buffer *buffer,
Timestamp time)
{
string buf = buffer->retrieveAllAsString();
json js = json::parse(buf);
auto msgHandler = ChatService::instance()->getHandler(js["msgid"].get<int>());
msgHandler(conn, js, time);
}
ChatService 提前将各个业务模块的代码放入了function
容器中,其内部有用哈希表记录了各种msgid
所对应的MsgHandler
(业务处理函数)。
因此每次接收消息时,通过getHandler(msgid)
得到对应的业务处理函数,再统一用msgHandler(conn, js, time);
来调用。某种角度上,getHandler
有点智能路由的意思。
可以看到,在构造函数内部就在哈希表中记录了 (消息类型 -> 绑定的回调函数)
ChatService::ChatService()
{
// 对各类消息处理方法的注册
_msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::loginHandler, this, _1, _2, _3)});
_msgHandlerMap.insert({REGISTER_MSG, std::bind(&ChatService::registerHandler, this, _1, _2, _3)});
_msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChatHandler, this, _1, _2, _3)});
_msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFriendHandler, this, _1, _2, _3)});
// 群组业务管理相关事件处理回调注册
_msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1, _2, _3)});
_msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1, _2, _3)});
_msgHandlerMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1, _2, _3)});
}
因此可以通过getHandler(int msgId)
获取函数,如果找不到改情况的理器,会返回一个默认的处理器。这里直接使用 lambda 表达式打印日志。
MsgHandler ChatService::getHandler(int msgId)
{
// 找不到对应处理器的情况
auto it = _msgHandlerMap.find(msgId);
if (it == _msgHandlerMap.end())
{
// 返回一个默认的处理器(lambda匿名函数,仅仅用作提示)
return [=](const TcpConnectionPtr &conn, json &js, Timestamp) {
LOG_ERROR << "msgId: " << msgId << " can not find handler!";
};
}
return _msgHandlerMap[msgId];
}