65.9K
CodeProject 正在变化。 阅读更多。
Home

数据构造框架

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2021年6月3日

CPOL

1分钟阅读

viewsIcon

4821

downloadIcon

87

自动将某个键映射到构造函数,以及它的用法

引言

有很多情况下,我们需要创建一个类的实例,但产生这种情况的代码并不知道要创建的对象的具体类型,只知道键。

例如,我们正在反序列化一个文件,该文件存储了一些文档,并遇到一个包含名为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);
};

这个类的用法非常简单

  1. 在某个地方应该描述具体的工厂
    using CarFactory = Factory<std::string, Car>;
  2. 将从Car继承的每个对象添加到CarFactory
    // adding NewCar to factory
    auto NewCarrAdded = CarFactory::add<NewCar>("NewCar");
  3. 创建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日:初始版本
© . All rights reserved.