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 @   kaizenly  阅读(629)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
历史上的今天:
2020-01-07 Cmder安装
2019-01-07 linux C access
2013-01-07 QTableWidget控件总结
2013-01-07 冒泡排序
打赏

喜欢请打赏

扫描二维码打赏

微信打赏

点击右上角即可分享
微信分享提示