数据构造框架





5.00/5 (1投票)
自动将某个键映射到构造函数,以及它的用法
引言
有很多情况下,我们需要创建一个类的实例,但产生这种情况的代码并不知道要创建的对象的具体类型,只知道键。
例如,我们正在反序列化一个文件,该文件存储了一些文档,并遇到一个包含名为OrangeCar
类的对象的记录,因此要读取它,我们可能会写成这样:
// Let's imagine that we have car hierarchy:
class Car{};
class OrangeCar : public Car{};
class RedCar : public Car{};
// and we want to read document this object from this hierarchy
Document read(std::istream in)
{
Document doc;
std::unique_ptr<Car> car;
std::string className = ...;
if (className == "OrangeCar")
{
car = std::make_unique<OrangeCar>();
}
else if (className == "RedCar")
{
car = std::make_unique<RedCar>();
}
.....
else if (className == "CarOf2058year")
{
car = std::make_unique<CarOf2058year>();
}
car->read(in); // for example
doc.cars.push_back(car);
}
新车将在2058年和2059年及以后加入,因此每次出现新车时,我们都需要修改这个函数。 最好能有类似这样的东西:
Document read(std::istream in)
{
Document doc;
std::unique_ptr<Car> car;
std::string className = ...;
auto car = CarFactory::createObject(className);
car->read(in); // for example
doc.cars.push_back(car);
}
让我们构建这样一个神奇的CarFactory
!
如果想向CarFactory
添加一个新的car
类时,能减少开销就更好了,类似这样就足够了:
// NewCar.cpp
class NewCar : public Car {};
// adding NewCar to factory
CarFactory::add<NewCar>("NewCar");
但是这个函数永远不会被调用,要调用它,我们需要初始化一些静态或全局变量,因此需要修改:
// adding NewCar to factory
auto NewCarrAdded = CarFactory::add<NewCar>("NewCar");
NewCarrAdded
的类型和大小无关紧要!编译器应该调用CarFactory::add<NewCar>("NewCar");
因此,CarFactory
的一个函数是已知的
class CarFactory
{
public:
struct Added {}; // Added size don't play matter at all
template<typename NewCar>
static Added add(std::string key);
static std::unique_ptr<Car> createObject(std::string key);
};
为了使这个类更通用,应该移除对类Car
和注册键类型的依赖
template<typename Key, typename ObjectBase>
class Factory
{
public:
using KeyType = Key;
using ObjectBase = ObjectBaseType;
using ObjectBasePtr = std::unique_ptr<ObjectBaseType>;
struct Added {}; // Added size don't play matter at all
template<typename Object>
static Added add(KeyType key);
static ObjectBasePtr createObject(const KeyType& key);
};
这个类的用法非常简单
- 在某个地方应该描述具体的工厂
using CarFactory = Factory<std::string, Car>;
- 将从
Car
继承的每个对象添加到CarFactory
// adding NewCar to factory auto NewCarrAdded = CarFactory::add<NewCar>("NewCar");
- 创建
car
auto car = CarFactory::createObject("NewCar");
实现
谁将创建对象? 像这样的函数:
template <typename Object>
std::unique_ptr<Object> = createObject() { return std::make_unique<Object>; }
因此,我们需要一个将键映射到函数的映射
template <typename Object>
using Function = std::unique_ptr<Object>(*)();
template <typename Key, typename Function>
using Map = std::unordered_map<Key, Function>;
所以Factory
变成了:
template<typename Key, typename ObjectBase>
class Factory
{
public:
using KeyType = Key;
using ObjectBase = ObjectBaseType;
using ObjectBasePtr = std::unique_ptr<ObjectBaseType>;
using CreatorFunction = std::function<ObjectBasePtr()>;
class Factories
{
public:
using Map = std::unordered_map<KeyType, CreatorFunction>;
void addCreator(Key key, CreatorFunction function)
{
m_factories[std::move(key)] = function;
}
CreatorFunction getCreatorFunction(const Key& key) const
{
if (auto found = m_factories.find(key); found != m_factories.end())
return found->second;
return nullptr;
}
const Map& getCreatorFunctions() const
{
return m_factories;
}
private:
Map m_factories;
};
struct Added {}; // Added size don't play matter at all
template<typename Object>
static Added add(KeyType key)
{
auto& factories = getFactories();
// CreatorFunction: creates Object and returns it as pointer to ObjectBase
static_assert(std::is_base_of<ObjectBase, Object>::value,
"Object must inherit ObjectBase");
static auto makeObject = [](){ return std::make_unique<Object>(); };
factories.addCreator(std::move(key), makeObject);
}
static ObjectBasePtr createObject(const KeyType& key)
{
auto& factories = getFactories();
if (auto function = factories.getCreatorFunction(key))
{
return function();
}
return {};
}
static typename Base::Factories& getFactories()
{
static typename Base::Factories factories;
return factories;
}
};
历史
- 2021年6月2日:初始版本