程序员人生 网站导航

C++利用反射和简单工厂模式实现业务模块解耦

栏目:框架设计时间:2015-04-03 08:17:30

1. 业务说明

为了便于说明,举1个简单的例子。假定现在有1个项目需要建立1个和银行交互的平台,目前只接入工商银行,后续接入其他银行,每一个银行的业务都有差异,报文格式可能也不1致。

这里只罗列几个扼要的流程,仅包括拼报文,发送报文,接收报文,解析报文,其余整体架构和后续处理等内容省略。

2. 初步设计

创建1个银行交互类 BankOpt,包括4个函数:

int setMsg(); //拼报文 int sendMsg(); //发送报文 int getMsg(); //接收报文 int parseMsg(); //解析报文

然后在每一个函数中通过if-else来判断具体是哪个银行,以后进行相应的处理。

这类设计在刚开发的时候非常方便,代码量少,但是如果后续需要接入另外1个银行时就需要改动BankOpt类,不符合设计模式中的开放-封闭原则。而且单个函数中将来可能会有大量的if-else,使代码可读性降落。

3. 简单工厂模式

通过简单工厂模式,我们可以创建1个专门的工厂类用于实例化1个适合的银行交互类,只需要这个银行交互类具有共同的接口便可。

首先,为了实现更好的复用,把各个银行交互类中相同的部份抽象出来,构成1个银行交互基类,代码以下:

class BaseBank { public: virtual int setMsg() = 0; virtual int sendMsg() = 0; virtual int getMsg() = 0; virtual int parseMsg() = 0; };

这里仅仅声明了4个纯虚函数,具体的业务逻辑在子类中实现。

创建两个银行交互子类GSBank(工商银行)和RMBank(人民银行),继承BaseBank,实现4个虚函数。

  • 创建1个工厂类
class BankFactory { public: BaseBank* createBank(const string& bank_name) { if (bank_name == “GSBank”) return new GSBank(); else if (bank_name == “RMBank”) return new RMBank(); } };

工厂类中有1个createBank函数,用于根据银行编码创建相应的实例并返回其基类指针,这样我们只需要通过基类指针调用相干函数便可。

  • 在主流程中调用
BankFactory bf; BaseBank* t = (BaseBank*)bf.createBank(bank_name); if (t == NULL) { cout << "银行编码毛病!" << endl; return 2; } t->setMsg(); t->sendMsg(); t->getMsg(); t->parseMsg();
  • 优缺点
    利用简单工厂模式,当我们后续接入另外的银行时,只需要添加具体的银行交互类,实现业务函数,然后在工厂类的createBank函数中添加1个else if子句。相对原来的设计已改进很多了,但是依然需要修改原来的工厂类的代码,没有完全实现解耦。

4. 反射

反射在java的1些框架中使用的比较多,而且用起来非常方便。C++本身其实不支持,但是我们可以摹拟1些简单的特性。

我们需要1种能够根据字符串动态获得对应的银行交互类的实例的方法。这样在工厂类的createBank方法中就能够根据字符串直接获得对应银行交互类的实例,而不需要再每次通过新增else if 子句来新增1个银行接口。

也就是说,利用反射和简单工厂模式,下次当我们需要新增1个银行接口的时候只需要新增1个银行交互类便可,不需要修改原来的任何代码,实现了业务上的解耦。

  • 如何在C++中实现反射
    1. 需要1个全局的map用于存储类的信息和创建实例的函数
    2. 需要反射的类需要提供1个用于创建本身实例的函数
    3. 利用类的静态变量在程序启动的时候会进行初始化来在全局map中将类名及创建实例的函数存入map中

相干代码以下:

typedef void* (*register_func)(); class Class { public: static void* newInstance(const string& class_name) { map<string, register_func>::iterator it = m_register.find(class_name); if (it == m_register.end()) return NULL; else return it->second(); } static void registerClass(const string& class_name, register_func func) { m_register[class_name] = func; } private: /* key is class name and value is function to create instance of class */ static map<string, register_func> m_register; }; class Register { public: Register(const string& class_name, register_func func) { Class::registerClass(class_name, func); } }; #define REGISTER_CLASS(class_name) class class_name##Register { public: static void* newInstance() { return new class_name; } private: static const Register reg; }; const Register class_name##Register::reg(#class_name,class_name##Register::newInstance);

还需要修改工厂类的createBank函数,利用Class的newInstance函数来创建实例:

BaseBank* createBank(const string& bank_name) { return (BaseBank*)Class::newInstance(bank_name); }

Class类中的m_register变量是static类型的map,相当于全局变量。

newInstance函数,传入类名,查找map,调用回调函数,返回1个对应类的实例。
registerClass函数传入类名和用于创建实例的回调函数并将信息存入全局的map中。

Register类只有1个构造函数,会调用Class的registerClass函数完成注册。

利用宏定义,在每个需要反射的类后面额外增加1个类,其中有1个Register类型的static const变量,这样在程序启动的时候就会完成初始化调用Register类的构造函数,完成注册。

以后只需要在需要反射的类,例如在工商银行交互类 GSBank 后面加上1条宏定义:
REGISTER_CLASS(GSBank) 就能够通过工厂类传入”GSBank”字符串取得工商银行交互类的实例。

5. 测试

工商银行

人民银行

通过传入不同的银行编码,会实例化不同的银行交互类,并且履行其对应的函数。

如果需要增加新的银行接口,例如农业银行,只需要新增1个NYBank类,实现具体的业务逻辑,不需要改动原来的任何代码,传入NYBank字符串,就会履行农业银行相干的处理流程。

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐