1. Business Description
For illustrative purposes, give a simple example. Suppose there is a project that needs to establish a platform to interact with the bank, currently only access ICBC, subsequent access to other banks, each bank's business is different, the message format may be inconsistent.
Here are just a few brief procedures, including the spelling of messages, sending messages, receiving messages, parsing messages, the rest of the overall architecture and subsequent processing and other content omitted.
2. Preliminary design
Create a bank interaction class bankopt, including four functions:
int setMsg(); //拼报文int sendMsg(); //发送报文int getMsg(); //接收报文int parseMsg(); //解析报文
Then in each function through the if-else to determine the specific bank, and then the corresponding processing.
This design is very convenient at the time of development, the code is small, but if the subsequent need to access another bank needs to change the Bankopt class, does not conform to the design mode of the open-closed principle. And there may be a lot of if-else in a single function in the future, reducing the readability of the code.
3. Simple Factory mode
With the simple factory model, we can create a specialized factory class to instantiate an appropriate bank interaction class, only the bank interaction class has a common interface.
First, in order to achieve better reuse, the same parts of the various bank interaction classes are abstracted out to form a bank interaction base class, the code is as follows:
class BaseBank{public: virtualintsetMsg0; virtualint0; virtualint0; virtualint0;};
Only four pure virtual functions are declared here, and the specific business logic is implemented in subclasses.
Create two bank interaction sub-classes Gsbank (ICBC) and Rmbank (People's bank), inherit Basebank, implement four virtual functions.
class BankFactory{public: createBank(conststring& bank_name) { if (bank_name == “GSBank”) returnnew GSBank(); elseif (bank_name == “RMBank”) returnnew RMBank(); }};
There is a Createbank function in the factory class that creates the corresponding instance based on the bank code and returns its base class pointer, so that we only need to invoke the correlation function from the base class pointer.
- Called in the main process
BankFactory bf;BaseBank*= (BaseBank*)bf.createBank(bank_name);if==NULL) { <<"银行编码错误!"<< endl; return2;}t->setMsg();t->sendMsg();t->getMsg();t->parseMsg();
- Pros and cons
With the simple factory model, when we subsequently access another bank, we only need to add a specific bank interaction class, implement the business function, and then add an else if clause in the Createbank function of the factory class. Compared to the original design has been improved a lot, but still need to modify the original factory class code, did not fully realize decoupling.
4. Reflection
Reflection is used more in some of the Java frameworks and is very handy to use. C + + itself is not supported, but we can emulate some simple features.
We need a way to dynamically obtain an instance of the corresponding bank interaction class based on the string. This way, in the Createbank method of the factory class, an instance of the corresponding bank interaction class can be obtained directly from the string, without having to add a new bank interface each time through the new else if clause.
In other words, using reflection and simple Factory mode, the next time we need to add a new bank interface, we just need to add a new bank interaction class, do not need to modify the original code, to achieve business decoupling.
- How to implement reflection in C + +
- A global map is required to store information about classes and functions that create instances
- The class that needs reflection needs to provide a function to create its own instance
- The static variables of the class are initialized when the program is started to store the class name and the function that created the instance in a map in the global map
The relevant code is as follows:
typedef void* (*register_func) ();classclass{ Public:Static void* Newinstance (Const string& Class_name) { Map<string, register_func>:: Iterator it = M_register.find (class_name);if(It = = M_register.end ())returnNULL;Else returnIt->second ();}Static voidRegisterClass (Const string& Class_name, Register_func func) {M_register[class_name] = func;}Private:/ * key is class name and value are function to create instance of class * / Static Map<string, register_func>M_register;};classregister{ Public: Register (Const string& Class_name, Register_func func) {Class::registerclass (class_name, func); }};#define REGISTER_CLASS (class_name) \ classClass_name# #Register {\ Public:Static void* Newinstance () {return Newclass_name; }Private:Static ConstRegister reg; };ConstRegister class_name# #Register:: Reg (#class_name, class_name# #Register:: newinstance);
You also need to modify the Createbank function of the factory class, using the class's newinstance function to create an instance:
BaseBank* createBank(conststring& bank_name) { return (BaseBank*)Class::newInstance(bank_name);}
The M_register variable in class is a map of the static type, equivalent to a global variable.
The Newinstance function, passing in the class name, looks for a map, invokes a callback function, and returns an instance of the corresponding class.
The RegisterClass function passes in the class name and the callback function used to create the instance and stores the information in a global map.
The Register class has only one constructor, which calls the class's registerclass function to complete the registration.
With a macro definition, add an extra class to each class that needs to be reflected, with a static const variable of register type, so that when the program starts, it completes the initialization of calling the Register class's constructor to complete the registration.
Then only the classes that need to be reflected, such as the Gsbank of ICBC interaction class, are followed by a macro definition:
Register_class (Gsbank) can get an instance of ICBC's interaction class by passing the "Gsbank" string to the factory class.
5. Testing
By passing in different bank codes, different bank interaction classes are instantiated and their corresponding functions are executed.
If you need to add a new bank interface, such as ABC, only need to add a Nybank class, the implementation of specific business logic, do not need to change the original code, passed the Nybank string, will be executed ABC related processing process.
C + + uses reflection and simple Factory mode for decoupling business modules