一个更好的自动注册工厂 | 知行一

一个更好的自动注册工厂

在几年前我介绍过一种C++11实现的自动注册工厂,这是工厂模式的一种优雅的实现。在这里我们需要明确一个概念就是工厂模式,它是如何优雅地解决一个产品族的创建问题。所谓产品族就是一个继承体系的产品,比如有一个产品Message,它是一个基类,有很多Message是从它派生而来的,比如有Message1,Message2,Message3…等很多产品。

这些产品的创建依赖于某个key,类似于这样:

这是一个典型的工厂方法,这种写法在产品不多的时候是没问题的,但是如果产品越来越多的时候,switch-case就会越来越长,导致难以维护。另外还存在一个问题,有的产品不是无参数的构造函数,如果有些产品依赖了不同的参数,那么这个工厂方法是无法满足需求的。

之前介绍的自动注册工厂解决了switch-case膨胀的问题,但是对于需要参数的产品的创建没有解决得很好,需要进一步改进。改进的办法是把参数作为factory的模板参数,这样就可以解决有参数需求的问题了。

下面是具体实现:

这是一个Messgae产品族的工厂类,有了这个类之后我们就可以很方便地创建各种产品了。下面是测试例子:

Message产品族有4类产品,有的是无参的,有的是多个参数的产品,现在都可以统一创建了,直接输入key和构造参数即可,这个key可以自行修改为int或者枚举变量。

需要注意的是msg4,因为它注册的时候提供了一个function,让function提供创建功能,以满足更灵活地需求。

有了这样一个工厂类之后我们就可以很好地解决产品族创建的问题了。你还可以基于此把它改成一个抽象工厂类,但我觉得你应该慎重考虑一下是否有必要,一般情况下工厂模式就够了,不需要引入更多的复杂性。

《一个更好的自动注册工厂》有11个想法

  1. 说真的,和原有的自动注册方法的区别。原有的也是支持多参构造的,若是仅指在produce时需要多参构造未注册的对象,这里的语义是值得商榷的。祁大,你说呢

    1. 原来的方法需要在注册的时候就要把实参传进去,这样不太好,有时候实参是要在后面根据上下文创建的。这个版本解决了这个问题。

        1. 不用静态变量也可以,反正这里必须定义一个唯一的全局变量。不用静态变量的话,多个cpp文件包含的话会有编译错误。

  2. 对版主“一个更好的自动注册工厂”的理解和优化

    看到版主“一个更好的自动注册工厂”(http://purecpp.org/?p=1700),很不错。用于试验实现不同图形的创建输出,进行了一些理解和优化。供交流参考。工程在VS2017中编译通过,主要包含Factory.cpp、FactoryTest.cpp、main.cpp等3个文件。

    //Factory.cpp
    #include
    #include

    //struct AbsProduct; //全局基类

    //改为注册工厂时传递基类
    template
    class factory
    {
    public:
    template
    class register_t //用于注册工厂的内嵌类型
    {
    public:
    // 使用具体工厂的无参或带参的默认构造函数
    register_t(const std::string& key)
    { // std::forward用于完美转发; &&为移动构造,避免临时对象析构和重新构造
    factory::get().map_.emplace(key, [](U&&… u){ return new T(std::forward(u)…);});
    }

    // 使用用户自定义的具体工厂的带参构造函数f
    register_t(const std::string& key, std::function&& f)
    {
    factory::get().map_.emplace(key, std::move(f));
    }
    };

    // 创建具体产品的函数
    inline AbsProduct* get_produce(const std::string& key, U&&… u)
    {
    if (map_.find(key) == map_.end())
    throw std::invalid_argument(“the factory key is not exist!”);

    return map_[key](std::forward(u)…);
    }

    // 得到唯一的具体工厂实例
    inline static factory& get()
    {
    static factory instance;
    return instance;
    }

    private:
    factory() {};
    factory(const factory&) = delete;
    factory(factory&&) = delete;

    // map
    std::map<std::string, std::function> map_;
    };

    #define REGISTER_FACTORY(AbsProduct, T, key) static factory::register_t REGISTER_FACTORY_VNAME(T)(key);
    #define REGISTER_FACTORY1(AbsProduct, T, key, …) static factory::register_t REGISTER_FACTORY_VNAME(T)(key);
    #define REGISTER_FACTORY2(AbsProduct, T, key, f, …) static factory::register_t REGISTER_FACTORY_VNAME(T)(key, f);

    #define REGISTER_FACTORY_VNAME(T) reg_factory_##T##_

    // 方便用户使用factory泛型工厂创建具体产品的全局辅助函数
    template
    inline AbsProduct* get_ConProduct(const std::string &key, Args &&… args){
    return factory::get().get_produce(key, std::forward(args)…);
    }

    //FactoryTest.cpp
    #include “Factory.cpp”
    #include
    using namespace std;

    class CPoint
    {
    public:
    CPoint(int xx = 0, int yy = 0) : x(xx), y(yy) {}
    int x; int y;

    friend ostream &operator<<(ostream &os, const CPoint &pt)
    {
    os << "(" << pt.x << ", " << pt.y << ")";
    return os;
    }
    };

    class Shape
    {
    public:
    virtual void Draw() {}
    virtual ~Shape() {}

    int m_nDrawType;
    CPoint m_ptStart;
    CPoint m_ptEnd;
    double m_dRadius;
    };

    class Rect : public Shape
    {
    public:
    Rect(int nDrawType, CPoint ptStart, CPoint ptEnd)
    {
    m_nDrawType = nDrawType;
    m_ptStart = ptStart;
    m_ptEnd = ptEnd;
    }

    void Draw() {
    std::cout << "m_nDrawType:" << m_nDrawType
    << ", m_ptStart:" << m_ptStart << ", m_ptEnd:" << m_ptEnd << "; \n";
    }
    };

    class Line : public Shape // 子类Line
    {
    public:
    Line(int nDrawType, CPoint ptStart, CPoint ptEnd)
    {
    m_nDrawType = nDrawType;
    m_ptStart = ptStart;
    m_ptEnd = ptEnd;
    }

    void Draw() {
    cout << "m_nDrawType:" << m_nDrawType
    << ", m_ptStart:" << m_ptStart << ", m_ptEnd:" << m_ptEnd << "; \n";
    }
    };

    class Circle : public Shape // 子类Circle
    {
    public:
    Circle(int nDrawType, CPoint ptCenter, double nRadius)
    {
    m_nDrawType = nDrawType;
    m_ptStart = ptCenter;
    m_dRadius = nRadius;
    }

    void Draw() {
    cout << "m_nDrawType:" << m_nDrawType
    << ", ptCenter:" << m_ptStart << ", m_dRadius:" << m_dRadius << "; \n";
    }
    };

    //通过下面宏调用factory泛型类注册具体工厂
    REGISTER_FACTORY2(Shape, Rect, "rect", [](int nDrawType, CPoint ptStart, CPoint ptEnd) {return new Rect(nDrawType, ptStart, ptEnd); }, int, CPoint, CPoint);
    REGISTER_FACTORY2(Shape, Line, "line", [](int nDrawType, CPoint ptStart, CPoint ptEnd) {return new Line(nDrawType, ptStart, ptEnd); }, int, CPoint, CPoint);
    REGISTER_FACTORY2(Shape, Circle, "circle", [](int nDrawType, CPoint ptStart, double nRadius) {return new Circle(nDrawType, ptStart, nRadius); }, int, CPoint, double);

    //main.cpp
    #include
    using namespace std;

    // 通过REGISTER_FACTORY、REGISTER_FACTORY1、REGISTER_FACTORY2等宏,
    // 调用factory泛型工厂类注册Factory1、Factory2、Factory3、Factory4等具体工厂
    #include “FactoryTest.cpp”

    int main()
    {
    // 通过辅助函数produce_msg,根据传入的参数个数和类型,匹配调用已注册的具体工厂,创建具体产品
    auto p1 = get_ConProduct(“rect”, 1, CPoint(3, 4), CPoint(5, 6));
    p1->Draw();

    auto p2 = get_ConProduct(“line”, 2, CPoint(3, 4), CPoint(5, 6));
    p2->Draw();

    auto p3 = get_ConProduct(“circle”, 3, CPoint(3, 4), 5.7);
    p3->Draw();

    system(“pause”);
    return 0;
    }

    /*
    m_nDrawType:1, m_ptStart:(3, 4), m_ptEnd:(5, 6);
    m_nDrawType:2, m_ptStart:(3, 4), m_ptEnd:(5, 6);
    m_nDrawType:3, ptCenter:(3, 4), m_dRadius:5.7;
    */

发表评论