Modern C++ 模板通用工厂

Modern C++ 模板通用工厂

1 简单应用示例

1.1 示例代码

#include <iostream>
#include <string>
using namespace std;

class Shape
{
public:
    virtual void calc_area() { cout << "call Shape::calc_area" << endl; }
};

class Circle : public Shape
{
public:
    virtual void calc_area() { cout << "call Circle::calc_area" << endl; }
};

class Square : public Shape
{
public:
    virtual void calc_area() { cout << "call Square::calc_area" << endl; }
};

class Rectangle : public Shape
{
public:
    virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; }
};

int main()
{
    Shape* pB = new Shape;
    pB->calc_area();  //call Shape::calc_area
    delete pB;

    Shape* pC = new Circle;
    pC->calc_area(); //call Circle::calc_area
    delete pC;

    Shape* pS = new Square;
    pS->calc_area(); //call Square::calc_area
    delete pS;

    Shape* pR = new Rectangle;
    pR->calc_area(); //call Rectangle::calc_area
    delete pR;

    system("pause");
}

1.2 分析说明

  1. 缺点:
    • 每种类型对象在使用时候自己负责创建
    • 子类多的话,代码很长
  2. 优点:运行时多态

2 简单工厂模式

2.1 示例代码

#include <iostream>
#include <string>
using namespace std;

class Shape
{
public:
    virtual void calc_area() { cout << "call Shape::calc_area" << endl; }
};

class Circle : public Shape
{
public:
    virtual void calc_area() { cout << "call Circle::calc_area" << endl; }
};

class Square : public Shape
{
public:
    virtual void calc_area() { cout << "call Square::calc_area" << endl; }
};

class Rectangle : public Shape
{
public:
    virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; }
};

class ShapeFactory
{
public:
    static Shape* createShape(std::string type)
    {
        if ("circle" == type)
        {
            return new Circle;
        }
        if ("square" == type)
        {
            return new Square;
        }
        if ("rectangle" == type)
        {
            return new Rectangle;
        }
        return nullptr;
    }
};

int main()
{
    Shape* ptr = ShapeFactory::createShape("circle");
    if (ptr != nullptr)
    {
        ptr->calc_area();  //call Circle::calc_area
        delete ptr;
    }

    system("pause");
}

2.2 分析说明

  1. 缺点
    • 每新增一种类型,都需要写个新类型的if-else(或switch-case)
  2. 优点
    • 使用工厂统一创建每种类型的对象

3 工厂 + 静态注册

3.1 示例代码

#include <string>
#include <iostream>
#include <unordered_map>
using namespace std;

class Shape
{
public:
    virtual void calc_area() { cout << "call Shape::calc_area" << endl; }
};

class Circle : public Shape
{
public:
    virtual void calc_area() { cout << "call Circle::calc_area" << endl; }
};

class Square : public Shape
{
public:
    virtual void calc_area() { cout << "call Square::calc_area" << endl; }
};

class Rectangle : public Shape
{
public:
    virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; }
};

typedef void* (*pCreateShape)(void);

class CShapeFactory
{
public:
    ~CShapeFactory() = default;

public:
    void* getShapeByName(const std::string& className)
    {
        auto iter = m_mapCShape.find(className);
        if (iter == m_mapCShape.end())
            return nullptr;

        return iter->second();
    }

    void registoryClass(const std::string& name, pCreateShape method) 
    {
        m_mapCShape.insert(make_pair(name, method));
    }

    static CShapeFactory& getInstance()
    {
        static CShapeFactory oFactory;
        return oFactory;
    }

private:
    CShapeFactory() {};

    std::unordered_map<std::string, pCreateShape> m_mapCShape;  // 引入map
};

// 注册动作类
class CRegisterAction
{
public:
    CRegisterAction(std::string className, pCreateShape pCreateFn)
    {
        CShapeFactory::getInstance().registoryClass(className, pCreateFn);
    }
};

#define REGISTER_SERVICE(className)                      \
    className* create##className() {                     \
        return new className;                            \
    }                                                    \
    CRegisterAction createRegister##className(           \
        #className, (pCreateShape)create##className)


REGISTER_SERVICE(Circle);
REGISTER_SERVICE(Square);
REGISTER_SERVICE(Rectangle);

int main()
{
    for (auto& item : { "Circle", "Square", "Rectangle" })
    {
        auto pShape = (Shape*)CShapeFactory::getInstance().getShapeByName(item);
        if (pShape != nullptr)
        {
            pShape->calc_area();
            delete pShape;
        }
    }

    system("pause");
}

/* result:
call Circle::calc_area
call Square::calc_area
call Rectangle::calc_area
请按任意键继续. . .
*/

3.2 分析说明

  1. 缺点
    • 每个类都需要注册,容易遗漏
  2. 优点
    • 优化掉了if-else和switch-case

4 CRTP工厂

4.1 示例代码

  1. factory.hpp

    #pragma once
    #include <memory>
    #include <functional>
    #include <unordered_map>
    #include <utility>
    
    // ref: http://www.nirfriedman.com/2018/04/29/unforgettable-factory/
    
    namespace prefab
    {
        namespace patterns
        {
            template<typename I, typename Identify, typename... Args>
            class factory
            {
                friend I;
    
                using K = decltype(Identify::key);  //typename Identify::key_type;
                using V = decltype(Identify::value);// typename Identify::value_type;
    
                struct key 
                {
                    key() {};
                    template<typename T>
                    friend struct registar;
                };
    
                factory() = default;
    
                using builder = std::function<std::unique_ptr<I>(Args...)>;
                using reader = V(*)();
    
                static auto& builders()
                {
                    static std::unordered_map<K, builder> container;
                    return container;
                }
    
                static auto& readers()
                {
                    static std::unordered_map<K, reader> container;
                    return container;
                }
            public:
                using self = factory<I, Identify, Args...>;
    
                template<typename... Ts>
                static std::unique_ptr<I> make(K const& k, Ts&&... args)
                {
                    auto it = builders().find(k);
                    if (it == builders().end()) return nullptr;
                    return it->second(std::forward<Ts>(args)...);
                }
    
                static std::pair<bool, V> identify(K const& k)
                {
                    std::pair<bool, V> result;
                    result.first = false;
                    auto it = readers().find(k);
                    if (it == readers().end()) return result;
                    result.first = true;
                    result.second = it->second();
                    return result;
                }
    
                template<typename T>
                struct registar : I
                {
                    friend T;
    
                    static bool register_()
                    {
                        const auto r = T::identify().key;
    
                        factory::builders()[r] = [](Args&&... args)->std::unique_ptr<I> {
                            return std::make_unique<T>(std::forward<Args>(args)...);
                        };
    
                        factory::readers()[r] = []()->V {
                            return T::identify().value;
                        };
                        return true;
                    }
    
                    static bool registered;
                private:
                    registar() : I(key{})
                    {
                        (void)registered;
                    }
                };
            };
    
            template<typename I, typename Identify, typename... Args>
            template<typename T>
            bool factory<I, Identify, Args...>::registar<T>::registered =
                factory<I, Identify, Args...>::registar<T>::register_();
        }
    }
    
    #if 0
    
    #include <iostream>
    #include <string>
    
    namespace prefab_patterns_example
    {
        template<typename I, typename Identify, typename... Args>
        using Factory = prefab::patterns::factory<I, Identify, Args...>;
    
        struct task_identify
        {
            std::string key;
            std::string value;
        };
    
        struct task :public Factory<task, task_identify, int>
        {
            task(self::key) {};
            virtual void execute() = 0;
        };
    
        struct print :task::registar<print>
        {
            print(int) {};
    
            void execute()override {
                std::cout << "print\n";
            };
    
            static task_identify identify() noexcept {
                (void)registered;  // MSVC has bug,use this force register
                return task_identify{ "print","task_print" };
            }
        };
    }
    
    #endif
    
  2. CShapeFactory.hpp

    #include <string>
    #include <iostream>
    #include "factory.hpp"
    
    template<typename I, typename Identify, typename... Args>
    using Factory = prefab::patterns::factory<I, Identify, Args...>;
    
    struct IShape_identify
    {
        std::string key;
        std::string value;
    };
    
    struct IShape : public Factory<IShape, IShape_identify>
    {
        IShape(self::key) {};
        virtual void calc_area() = 0;
    };
    
    struct Circle : public IShape::registar<Circle>
    {
    public:
        Circle() {}
    
        virtual void calc_area() { std::cout << "call Circle::calc_area" << std::endl; }
    
        static IShape_identify identify() noexcept {
            return IShape_identify{ "C", "Circle" };
        }
    };
    
    struct Square : public IShape::registar<Square>
    {
    public:
        Square() {}
    
        virtual void calc_area() { std::cout << "call Square::calc_area" << std::endl; }
    
        static IShape_identify identify() noexcept {
            return IShape_identify{ "S", "Square" };
        }
    };
    
    struct Rectangle : public IShape::registar<Rectangle>
    {
    public:
        Rectangle() {}
    
        virtual void calc_area() { std::cout << "call Rectangle::calc_area" << std::endl; }
    
        static IShape_identify identify() noexcept {
            return IShape_identify{ "R", "Rectangle" };
        }
    };
    
  3. main.cpp

    #include "CShapeFactory.hpp"
    
    int main()
    {
        for (auto& item : { "C", "S", "R" })
        {
            auto target = IShape::make(item);
            if (target != nullptr)
            {
                target->calc_area();
            }
        }
    
        system("pause");
    }
    
    /* result:
    call Circle::calc_area
    call Square::calc_area
    call Rectangle::calc_area
    请按任意键继续. . .
    */
    

4.2 分析说明

关于CRTP,详见随笔《C++ CRTP》

4.3 过程中问题

  1. 编译错误:C7510 “registar”: 模板从属名称的使用必须以“模板”为前缀

    解决方案:本地VS2019,关闭工程的“符合模式”即可(默认开启),如下图:

posted @ 2022-01-07 20:39  kaizenly  阅读(455)  评论(0编辑  收藏  举报
打赏