背景
以开发一个收发各类消息的APP为例。
我们会遇到,针对不同的消息类型,做出对应的处理的情况。
但是可能这个模块的架构并没有设计的很好,又随着功能的开发,经常会需要消新增消息类型。随着息类型的新增,这块消息处理的代码越来越复杂,比如一个文件上万行,最终难以扩展以及维护.
简单方案举例
最奔放的处理方式,代码难以阅读以及扩展
直接使用if else处理不同的消息类型
func recvMsg(msgType) {
if (msgType == "Text") {
// block of code to be executed
} else if (msgType == "Voice") {
// block of code to be executed
} else {
// block of error handling code to be executed
}
}
func sendMsg(msgType) {
if (msgType == "Text") {
// block of code to be executed
} else if (msgType == "Voice") {
// block of code to be executed
} else {
// block of error handling code to be executed
}
}
代码可读性强,但仍较难扩展以及维护
使用switch case处理不同的消息类型
func sendMsg(msgType) {
switch(msgType) {
case "Text":
// block of code to be executed
break;
case "Voice":
// block of code to be executed
break;
default:
// block of error handling code to be executed
}
}
func recvMsg(msgType) {
switch(msgType) {
case "Text":
// block of code to be executed
break;
case "Voice":
// block of code to be executed
break;
default:
// block of error handling code to be executed
}
}
IOC方案
好的设计会增加代码量,可能初看会觉得复杂,但是当你了解这个设计之后,又会觉得逻辑无比清晰,无论排查问题或者新增功能都得心应手。
方案对比
上述的简单方案,消息的收发模块与执行各个消息处理模块耦合在一起,导致消息模块的代码随着消息类型的增加,越来越庞大。
IOC方案将消息的收发模块与各个消息处理模块解耦,消息收发模块不再持有消息处理模块对象,转而持有IOC容器对象。
详细设计
类图
- 主要逻辑
- 控制反转
将MsgManager对各个MsgHandler的依赖,转化为对IOC容器的依赖,同时各个MsgHandler依赖IOC容器。MsgManager不需要包含MsgHandler_Text的头文件,同时MsgHandler_Text也不需要包含MsgManager的头文件。MsgManager主要工作就是分发消息到各个Handler,无论后续新增多少个Handler,MsgManager的实现都无需改动。 - 自注册
MsgHandler_Text通过ObjectRegistrar在自己文件中实现在MsgHandlerContext中的注册。
代码介绍
核心容器代码
// BaseFactory.h
template <typename T>
class HandlerContext {
public:
HandlerContext(HandlerContext const&) = delete;
HandlerContext(HandlerContext &&) = delete;
HandlerContext& operator=(HandlerContext const&) = delete;
HandlerContext& operator=(HandlerContext &&) = delete;
T* getClass(const std::string& msgType) //按照消息类型获取Handler
{
auto iter = mHandlers.find(msgType);
if (iter != mHandlers.end())
{
return iter->second;
}
return nullptr;
}
bool Register(std::string msgType, T* ptr) //按照消息类型注册Handler
{
if (ptr == nullptr)
{
return false;
}
auto ret_pair = mHandlers.insert(std::make_pair(msgType, ptr));
return ret_pair.second;
}
protected:
HandlerContext() = default;
~HandlerContext() = default;
std::unordered_map<std::string, T*> mHandlers;//通过保存自注册的处理对象,实现对象的随用随取
};
template <typename Context, typename T>
class ObjectRegistrar {
public:
ObjectRegistrar(std::string msgType) { Context::shareInstance()->Register(msgType, T::shareInstance());
}
};
业务代码举例
// MsgHandlerContext.h
#define REGISTER_MSG_HANDLER(MsgType, Handler) \
static ObjectRegistrar<MsgHandlerContext, Handler> global_##MsgType##Registrar(#MsgType); //通过静态变量的构造方法实现自注册
// MsgManager.cpp
void MsgManager::recvMsg()
{
auto* handlerContext = MsgHandlerContext::shareInstance();
//根据消息类型直接分发到对应的Handler,不在需要复杂的if else逻辑或者switch case逻辑
auto handler = handlerContext->getClass(msgType);
if (handler)
{
auto sendMap = handler->recvMsg();
}
}
// MsgHandler_Text.cpp
REGISTER_MSG_HANDLER(Text, MsgHandler_Text) //注册当前类
MsgHandler_Text* MsgHandler_Text::shareInstance() {
static MsgHandler_Text shareInstance;
return &shareInstance;
}
void MsgHandler_Text::recvMsg() {
// block of code to be executed
}
void MsgHandler_Text::sendMsg() {
// block of code to be executed
}
优点
- 可读性强,新增消息类型,均在各个业务对应的文件中实现。
- 易扩展,新增消息类型,只需新增对应消息类型命名的文件即可。