工厂模式作为设计模式中的"老熟人",一直是C++开发者在项目中处理对象创建的利器。但你是否也曾为每次添加新产品都要写一堆重复的注册代码而烦恼?今天就来教你一个黑科技——用宏定义简化工厂模式的实现,让代码量骤减,扩展性拉满!
什么是工厂模式?
先快速回顾一下工厂模式的核心思想:将对象的创建与使用分离。通过一个统一的"工厂"类,根据不同的条件创建不同的"产品"实例,而客户端无需知道具体产品的创建细节。
比如你要开发一个图形库,有圆形、矩形、三角形等形状(产品),工厂模式就可以通过"形状工厂"统一管理这些形状的创建,客户端只需告诉工厂"我要一个圆形",就能得到对应的实例。
传统实现的痛点
传统的工厂模式实现中,每次添加新的产品都要做两件事:
- 定义产品类(继承自产品基类)
- 在工厂中添加注册该产品的代码
当产品数量增多时,重复的注册代码会变得非常冗余,而且容易出错(比如拼写错误、忘记注册等)。
有没有办法让注册过程自动化?宏定义就是解决这个问题的关键!
用宏定义实现"自动注册"
下面直接上代码,看看如何用宏定义简化工厂模式的实现:
1. 产品基类
首先定义所有产品的共同接口:
#include <iostream>
#include <map>
#include <string>
#include <memory>
// 产品基类 - 所有具体产品的共同接口
class Product {
public:
// 虚析构函数,确保派生类析构正确调用
virtual ~Product() = default;
// 纯虚函数,定义产品的核心操作
virtual void operation() const = 0;
};
2. 工厂类
工厂类负责管理产品的创建逻辑,这里用单例模式存储产品类型与创建函数的映射:
class Factory {
public:
// 定义创建函数的指针类型
using CreateFunc = std::unique_ptr<Product>(*)();
// 注册产品:将类型与创建函数关联
static void registerProduct(const std::string& type, CreateFunc func) {
getCreators()[type] = func;
}
// 创建产品:根据类型字符串生成对应实例
static std::unique_ptr<Product> createProduct(const std::string& type) {
auto it = getCreators().find(type);
if (it != getCreators().end()) {
return it->second(); // 调用创建函数
}
return nullptr; // 未找到对应类型
}
private:
// 单例模式:存储类型到创建函数的映射表
static std::map<std::string, CreateFunc>& getCreators() {
static std::map<std::string, CreateFunc> creators;
return creators;
}
// 私有构造函数,禁止实例化
Factory() = default;
};
3. 关键宏定义
这一步是核心!用宏定义实现产品的自动注册:
// 宏定义:自动注册产品到工厂
#define REGISTER_PRODUCT(ProductClass, TypeName) \\
class ProductClass##Registrar { \\
public: \\
ProductClass##Registrar() { \\
// 注册产品类型和创建函数 \\
Factory::registerProduct(TypeName, []() { \\
return std::make_unique<ProductClass>(); \\
}); \\
} \\
}; \\
// 静态注册器实例,程序启动时自动执行注册 \\
static ProductClass##Registrar g_##ProductClass##Registrar;
这个宏做了三件事:
- 定义一个专属的注册器类(类名包含产品类名,避免冲突)
- 在注册器的构造函数中,将产品类型和创建函数注册到工厂
- 声明静态注册器实例,利用静态变量的特性在程序启动时自动完成注册
4. 具体产品实现
有了上面的基础,添加具体产品就变得非常简单:
// 具体产品A
class ConcreteProductA : public Product {
public:
void operation() const override {
std::cout << "执行 ConcreteProductA 的操作" << std::endl;
}
};
// 一行代码完成注册
REGISTER_PRODUCT(ConcreteProductA, "ProductA")
// 具体产品B
class ConcreteProductB : public Product {
public:
void operation() const override {
std::cout << "执行 ConcreteProductB 的操作" << std::endl;
}
};
// 一行代码完成注册
REGISTER_PRODUCT(ConcreteProductB, "ProductB")
5. 测试代码
最后看看如何使用这个工厂:
int main() {
// 通过工厂创建产品(只需知道类型字符串)
auto productA = Factory::createProduct("ProductA");
auto productB = Factory::createProduct("ProductB");
auto invalidProduct = Factory::createProduct("InvalidProduct");
// 调用产品操作
if (productA) productA->operation(); // 输出:执行 ConcreteProductA 的操作
if (productB) productB->operation(); // 输出:执行 ConcreteProductB 的操作
if (!invalidProduct) {
std::cout << "无法创建未知类型的产品" << std::endl;
}
return 0;
}
宏定义的优势在哪?
- 消除重复代码:无需为每个产品编写注册逻辑,一行宏调用搞定
- 降低出错概率:标准化注册流程,避免手动注册的拼写错误
- 提高开发效率:添加新产品时只需关注产品本身的实现
- 增强可维护性:注册逻辑集中在宏定义中,修改时只需调整一处
- 真正解耦:客户端无需包含具体产品的头文件,只需知道类型标识符
注意事项
虽然宏定义很方便,但也要注意:
- 宏定义会增加代码的间接性,调试时可能需要查看宏展开后的代码
- 确保产品类名和类型字符串的唯一性,避免冲突
- 静态变量的初始化顺序可能导致问题(不过在单线程程序中通常无需担心)
这种实现方式特别适合大型项目,当你需要管理几十甚至上百种相似类型的对象时,会显著简化代码结构。下次再用工厂模式,不妨试试这个宏定义的技巧,让代码更简洁、更优雅!
7592

被折叠的 条评论
为什么被折叠?



