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 分析说明
- 缺点:
- 每种类型对象在使用时候自己负责创建
- 子类多的话,代码很长
- 优点:运行时多态
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 分析说明
- 缺点
- 每新增一种类型,都需要写个新类型的if-else(或switch-case)
- 优点
- 使用工厂统一创建每种类型的对象
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 分析说明
- 缺点
- 每个类都需要注册,容易遗漏
- 优点
- 优化掉了if-else和switch-case
4 CRTP工厂
4.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
-
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" }; } };
-
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 过程中问题
-
编译错误:C7510 “registar”: 模板从属名称的使用必须以“模板”为前缀
解决方案:本地VS2019,关闭工程的“符合模式”即可(默认开启),如下图: