本文最后更新于:9 分钟前
技术背景
同一类设备往往有多个具体实现。例如,不同品牌的相机可以共用 Camera 接口,不同品牌的雷达可以共用 Radar 接口。调用者通过接口执行操作,但创建对象时仍需确定具体子类。如果每个使用位置都自行判断品牌并构造对象,类型选择和具体类名就会散落在各处,新增类型时也需要逐处调整。
工厂将这部分创建逻辑集中到统一入口:调用者提供类型标识,工厂返回相应的基类指针,后续操作沿用基类接口。本文中的“纯静态工厂”指注册和创建操作都通过静态成员函数提供,使用时直接调用 CameraFactory::create(brand),无需先创建工厂对象。C++ 的静态成员可以通过类名访问,静态成员函数没有 this 指针。C++ 标准草案:静态成员
统一入口内部也可以用 switch 或 if 选择具体类型;本文采用注册表保存“类型标识 → 创建函数”的对应关系。注册时登记如何构造子类,创建时查表并执行相应函数,使工厂的查找逻辑与各子类的创建表达式分开。这里用 std::function 统一保存创建函数:它按给定调用签名封装可调用对象,因此不同子类的创建 lambda 可以存入同一种注册表。C++ 标准草案:std::function
基本思路
先封装产品接口类,由各类型子类继承接口类;再提供静态工厂。外部调用者通过工厂的 create() 获取产品指针,然后调用接口类中定义的函数。
工厂内部保存一张注册表,将产品类型映射到对应的创建函数。调用时只需要指定类型,不必在调用处直接写出具体子类。
下面从相机工厂开始,逐步介绍模板封装和不同的注册写法。
基础版:相机工厂
CameraFactory 使用 Camera::Brand 区分相机类型,创建函数统一返回 std::shared_ptr<Camera>。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #pragma once #include <memory> #include <string> #include <unordered_map> #include <functional> #include "Camera.h" #include "Logger.hpp"
class CameraFactory { public: using Creator = std::function<std::shared_ptr<Camera>()>; static void registerCreator(Camera::Brand brand, Creator creator){ getRegistry()[brand] = creator; }; static std::shared_ptr<Camera> create(Camera::Brand brand){ auto it = getRegistry().find(brand); if (it != getRegistry().end()) { return it->second(); } SPD_ERROR("Camera Brand [{}] has not been registered! May be not included when compile!", static_cast<int>(brand)); return nullptr; }; private: static std::unordered_map<Camera::Brand, Creator>& getRegistry(){ static std::unordered_map<Camera::Brand, Creator> registry; return registry; }; };
#define REGISTER_CAMERA_FACTORY(CameraType, brand) \ namespace { \ struct Register##CameraType { \ Register##CameraType() { \ CameraFactory::registerCreator(Camera::Brand::brand, []() { \ return std::make_shared<CameraType>(); \ }); \ } \ }; \ static Register##CameraType _RegisterInstance##CameraType; \ }
|
这段代码分成三个部分:
registerCreator():把创建函数存入对应品牌的注册项。
create():查找品牌,找到后执行创建函数;未找到时输出日志并返回 nullptr。
getRegistry():通过函数内的静态变量保存注册表。
REGISTER_CAMERA_FACTORY 把子类注册过程封装成宏。宏定义一个注册辅助类,在它的构造函数中调用 registerCreator(),再通过静态对象触发注册。传入的是创建相机的 lambda,实际创建相机的操作发生在调用该 lambda 时。
模板形式:复用工厂逻辑
将产品基类和类型键提取为模板参数,就可以把相同的注册、查找逻辑复用于其他产品。
T 表示产品基类。
U 表示区分产品类型的键,默认使用 std::string。
Creator 表示返回 std::shared_ptr<T> 的创建函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #pragma once #include <memory> #include <string> #include <unordered_map> #include <functional> #include <iostream>
template <typename T, typename U = std::string> class GeneralModelFactory { public: using Creator = std::function<std::shared_ptr<T>()>; static void registerCreator(U type, Creator creator){ getRegistry()[type] = creator; }; static std::shared_ptr<T> create(U type){ auto it = getRegistry().find(type); if (it != getRegistry().end()) { return it->second(); } std::cerr << "Model Type [" << type << "] has not been registered!" << std::endl; return nullptr; }; private: static std::unordered_map<U, Creator>& getRegistry(){ static std::unordered_map<U, Creator> registry; return registry; }; };
#define BASEMODELCLASS_CREATE_FACTORY(BaseClassName, TypeName) \ using BaseClassName##Factory = GeneralModelFactory<BaseClassName, TypeName>
#define SUBMODELCLASS_REGISTER_FACTORY(BaseClassName, SubClassName, TypeKey) \ namespace { \ struct Register##SubClassName { \ Register##SubClassName() { \ BaseClassName##Factory::registerCreator(TypeKey, []() { \ return std::make_shared<SubClassName>(); \ }); \ } \ }; \ static Register##SubClassName _RegisterInstance##SubClassName; \ }
|
BASEMODELCLASS_CREATE_FACTORY 用于声明基类对应的工厂别名;SUBMODELCLASS_REGISTER_FACTORY 放在子类 .cpp 中,将类型键和子类创建函数关联起来。
在基类头文件中声明工厂
以雷达为例,使用 Radar::Brand 区分类型,在基类定义后声明工厂:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public: enum Brand { UNKNOWN = 0, BRAND_A = 1, BRAND_B = 2, BRAND_C = 3, }; public: Brand brand_; std::string ip; int port{0};
public: Radar(Brand brand) : brand_(brand){}; void SetInfo(std::string ip, int port = 0) { if (!ip.empty()) this->ip = ip; if (port > 0) this->port = port; };
virtual ~Radar() = default; virtual int Open() = 0; virtual int Close() = 0; virtual int GetVersion(std::string &version) = 0; virtual int GetNTP(NTPInfo &info) = 0; virtual int SetNTP(NTPInfo info) = 0; };
BASEMODELCLASS_CREATE_FACTORY(Radar, Radar::Brand);
|
在子类 cpp 中注册
在具体子类的实现文件中登记类型:
1
| SUBMODELCLASS_REGISTER_FACTORY(Radar, RadarBrandB, Radar::Brand::BRAND_B);
|
这样,具体子类的注册放在自己的实现文件附近,调用者仍通过对应基类工厂获取产品指针。
在头文件中使用 inline 注册
在头文件中注册时,可以使用 C++17 的 inline 变量,通过初始化表达式中的 lambda 完成注册。
1 2 3 4 5 6 7
| #define SUBCLASS_REGISTER_FACTORY_INLINE(BaseClassName, SubClassName, TypeName, type) \ inline bool _register_##SubClassName = [](){ \ GeneralModelFactory<BaseClassName, TypeName>::registerCreator(TypeName::type, [](){ \ return std::make_shared<SubClassName>(); \ }); \ return true; \ }();
|
这里省去了单独的注册辅助类,通过变量的初始化表达式调用 registerCreator()。
同一个子类注册多个类型
前面的宏直接使用子类名生成注册辅助类和静态对象的名字。当同一个 .cpp 中需要为同一个子类注册多个类型时,可以把行号拼进这些名字,区分不同的注册位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #define MODEL_CONCAT_IMPL(a, b) a##b #define MODEL_CONCAT(a, b) MODEL_CONCAT_IMPL(a, b)
#define SUBMODELCLASS_REGISTER_FACTORY(BaseClassName, SubClassName, TypeKey) \ namespace { \ struct MODEL_CONCAT(Register_, MODEL_CONCAT(SubClassName, __LINE__)) { \ MODEL_CONCAT(Register_, MODEL_CONCAT(SubClassName, __LINE__))() { \ BaseClassName##Factory::registerCreator( \ TypeKey, []() { return std::make_shared<SubClassName>(); } \ ); \ } \ }; \ static MODEL_CONCAT(Register_, MODEL_CONCAT(SubClassName, __LINE__)) \ MODEL_CONCAT(_RegisterInstance_, MODEL_CONCAT(SubClassName, __LINE__)); \ }
|
这一版本沿用前面的注册过程,通过不同的名字区分注册辅助类和静态对象。MODEL_CONCAT_IMPL 负责拼接,外层 MODEL_CONCAT 配合 __LINE__ 生成各注册位置的名字。