背景
在构建C++框架时经常用到一项基础技术, 就是根据一个类名称字符串动态创建类对象, 这样可以实现即为彻底的解耦合, 比如常规的基于工厂方法的创建实例代码需要在工厂实现文件中去include各个子类头文件,并需要显式的去调用子类构造函数, 如下代码示例:
#include "A.h"
#include "B.h"
#include "C.h"
Base* Factory::CreateObject()
{
Object* obj = nullptr;
if(xxx)
{
obj = new A();
}
else if(xxx)
{
obj = new B();
}
else
{
obj = new C();
}
return obj;
}
这种写法每加入一个新的子类,都需要修改这个Create入口, 且无法实现配置化
而如果能支持一种根据类名动态创建类实例的能力,则将大大改善
如下示例,className可以通过配置化获取, 不需要显示调用子类的构造函数
Base* b = CreateObject(className);
b->Execute();
实现原理
核心元素:
一个管理支持动态创建能力类的工厂ObjectFactory类, 其中核心成员是一个map:
key:类名字符串 --> value:创建该类对象的函数指针
并提供2个核心函数:
1.注册某个类到map RegisterObj(类名字符串, 创建该类的函数指针)
2.创建对象 CreateObj(类名字符串)
类图:
需要支持该能力的类继承Object类, 并注册创建自身实例的静态函数指针到ObjectFactory
可以利用宏定义增加易用性
自动注册时机原理:
利用全局静态变量的初始化在main函数之前的原理, 对于此类支持动态创建的类,都增加一个静态变量初始化时驱动调用注册
static int g_tmp_xxx = ObjectFactory::GetInstance().RegisterObj("ClassName", ClassName::CreateObj);
实现代码
包含核心代码+简化实用的宏+单元测试
代码风格基于C++ 11
ObjectFactory.h
#ifndef __OBJECTFACTORY_H__
#define __OBJECTFACTORY_H__
#include <map>
#include <string>
#include <functional>
class Object
{
public:
virtual std::string GetClassName() const = 0;
};
class ObjectFactory
{
public:
static ObjectFactory& GetInstance();
using ObjCreator = std::function<Object*(void)>;
int RegisterObj(const std::string& className, ObjCreator objCreator);
Object* CreateObj(const std::string& className);
private:
ObjectFactory();
~ObjectFactory();
ObjectFactory(const ObjectFactory&) = delete;
std::map<std::string, ObjCreator> creatorMap_;
};
#endif
ObjectFactory.cpp
#include "ObjectFactory.h"
using namespace std;
ObjectFactory& ObjectFactory::GetInstance()
{
static ObjectFactory instance_;
return instance_;
}
int ObjectFactory::RegisterObj(const std::string& className, ObjCreator objCreator)
{
creatorMap_.insert(make_pair(className, objCreator));
return 0;
}
Object* ObjectFactory::CreateObj(const std::string& className)
{
auto it = creatorMap_.find(className);
if(it == creatorMap_.end())
{
return nullptr;
}
return it->second();
}
ObjectFactory::ObjectFactory()
{
}
ObjectFactory::~ObjectFactory()
{
}
CreateObj.h 这是增加易用性宏
CREATE_OBJECT提供了模板版本增加类型转换易用性
#ifndef __CREATEOBJ_H__
#define __CREATEOBJ_H__
#include "ObjectFactory.h"
#define DECLARE_DYNAMIC_CREATE(CLASS_NAME) \
static Object* CreateObj(); \
std::string GetClassName() const override;
#define DYNAMIC_CREATE(CLASS_NAME) \
Object* CLASS_NAME::CreateObj() \
{ \
return static_cast<Object*>(new CLASS_NAME()); \
} \
\
std::string CLASS_NAME::GetClassName() const \
{ \
return #CLASS_NAME; \
} \
\
static int g_tmp_##CLASS_NAME = \
ObjectFactory::GetInstance().RegisterObj(#CLASS_NAME, CLASS_NAME::CreateObj);
template<typename T>
T* CREATE_OBJECT(const std::string& className)
{
return dynamic_cast<T*>(ObjectFactory::GetInstance().CreateObj(className));
}
#endif
测试代码
Person.h 支持动态创建声明DECLARE_DYNAMIC_CREATE
#ifndef __PERSON_H__
#define __PERSON_H__
#include "CreateObj.h"
#include <string>
class Person : public Object
{
public:
DECLARE_DYNAMIC_CREATE(Person)
virtual void Do();
void IntroduceSelf();
std::string name_;
int age_;
};
#endif
Person.cpp
支持动态创建实现宏 DYNAMIC_CREATE
#include "Person.h"
#include <iostream>
using namespace std;
DYNAMIC_CREATE(Person)
void Person::IntroduceSelf()
{
cout << "I am "<<name_<<", "<<age_<<" years old"<<endl;
}
void Person::Do()
{
cout << "Do nothing" << endl;
}
Student.h
#ifndef __STUDENT_H__
#define __STUDENT_H__
#include "Person.h"
class Student : public Person
{
public:
DECLARE_DYNAMIC_CREATE(Student)
void Do() override;
};
#endif
Student.cpp
#include "Student.h"
#include "Person.h"
#include <iostream>
using namespace std;
DYNAMIC_CREATE(Student)
void Student::Do()
{
cout << "I am a student, I am learning" << endl;
}
测试用例
没有引入Student.h, 但可以通过类名字符串"Student"创建实例执行
#include "gtest.h"
#include "Person.h"
#include "CreateObj.h"
#include <vector>
using namespace std;
class CreateObjTest : public ::testing::Test
{
};
void PersonExecute(Person* p)
{
p->name_="wangliang";
p->age_ = 18;
p->IntroduceSelf();
p->Do();
}
TEST_F(CreateObjTest, TestCreatePerson)
{
Person* p = CREATE_OBJECT<Person>("Person");
if(p == nullptr)
{
GTEST_FAIL();
return;
}
EXPECT_TRUE(p->GetClassName() == "Person");
PersonExecute(p);
}
TEST_F(CreateObjTest, TestCreateStudentAsPerson)
{
Person* p = CREATE_OBJECT<Person>("Student");
if(p == nullptr)
{
GTEST_FAIL();
return;
}
EXPECT_TRUE(p->GetClassName() == "Student");
PersonExecute(p);
}
运行效果: