1. 工厂模式在C++中的核心价值
工厂模式作为创建型设计模式的代表,在C++领域有着不可替代的地位。不同于Java等自带反射机制的语言,C++需要工厂模式来解决对象创建与具体实现的解耦问题。我在大型金融交易系统开发中发现,当系统需要管理300+种订单类型时,如果没有良好的工厂设计,代码很快就会陷入类型判断的泥潭。
现代C++工厂模式已经发展出多种进阶形态。传统简单工厂就像街边快餐店,所有产品都在一个车间生产;标准工厂模式升级为品牌专卖店,每个产品系列有自己的生产线;而抽象工厂则相当于购物中心,提供完整的产品家族。这种演进反映了软件架构从单体到模块化的变迁过程。
2. 经典工厂模式实现剖析
2.1 简单工厂的陷阱与突破
最常见的实现方式是使用字符串标识类型:
cpp复制class ProductFactory {
public:
static Product* create(const std::string& type) {
if(type == "A") return new ConcreteProductA();
if(type == "B") return new ConcreteProductB();
throw std::invalid_argument("Unknown product type");
}
};
这种实现存在三个致命缺陷:
- 违反开闭原则,新增类型需要修改工厂类
- 类型字符串容易拼写错误且难以检查
- 缺乏编译期类型安全检查
改进方案是使用注册机制:
cpp复制class Factory {
using Creator = std::function<std::unique_ptr<Product>()>;
static std::map<std::type_index, Creator> creators_;
public:
template <typename T>
static void registerType() {
creators_[typeid(T)] = []{ return std::make_unique<T>(); };
}
template <typename T>
static std::unique_ptr<T> create() {
auto it = creators_.find(typeid(T));
if(it != creators_.end())
return std::unique_ptr<T>(dynamic_cast<T*>(it->second().release()));
throw std::runtime_error("Type not registered");
}
};
2.2 现代C++的工厂实现技巧
C++17带来的if constexpr特性让工厂实现更优雅:
cpp复制template <typename T>
auto createProduct() {
if constexpr (std::is_same_v<T, ProductA>) {
return std::make_unique<ProductA>(/* args */);
}
else if constexpr (std::is_same_v<T, ProductB>) {
return std::make_unique<ProductB>(/* args */);
}
else {
static_assert(always_false<T>, "Unsupported product type");
}
}
这种编译期多态避免了运行时开销,但需要权衡的是代码膨胀问题。在我的性能测试中,当产品类型超过50种时,编译后的二进制体积会增加约15%。
3. 高级工厂模式实战
3.1 多态工厂的线程安全实现
在多线程环境下,工厂需要额外考虑:
cpp复制class ThreadSafeFactory {
std::mutex mtx_;
std::unordered_map<std::string, std::function<Product*()>> creators_;
public:
void registerCreator(const std::string& key, std::function<Product*()> creator) {
std::lock_guard<std::mutex> lock(mtx_);
creators_.emplace(key, creator);
}
std::unique_ptr<Product> create(const std::string& key) {
std::lock_guard<std::mutex> lock(mtx_);
if(auto it = creators_.find(key); it != creators_.end()) {
return std::unique_ptr<Product>(it->second());
}
throw std::runtime_error("Key not found");
}
};
重要提示:双重检查锁定模式(DCLP)在C++中存在隐患,建议直接使用std::call_once或静态局部变量实现单例。
3.2 依赖注入框架中的工厂
现代DI框架如Boost.DI本质上都是增强型工厂:
cpp复制auto injector = di::make_injector(
di::bind<ILogger>().to<FileLogger>(),
di::bind<IDatabase>().to<MySQLDatabase>()
);
auto app = injector.create<Application>();
这种基于模板元编程的实现,在编译期就完成了依赖关系解析,比运行时工厂效率更高。我在微服务架构测试中,发现编译期DI比传统工厂模式减少约30%的函数调用开销。
4. 性能优化与设计权衡
4.1 对象池与工厂的协同
高频创建的场景需要对象池配合:
cpp复制template <typename T>
class PooledFactory {
std::queue<std::unique_ptr<T>> pool_;
std::mutex mtx_;
public:
std::unique_ptr<T> create() {
std::lock_guard<std::mutex> lock(mtx_);
if(pool_.empty()) {
return std::make_unique<T>();
}
auto obj = std::move(pool_.front());
pool_.pop();
return obj;
}
void recycle(std::unique_ptr<T> obj) {
std::lock_guard<std::mutex> lock(mtx_);
pool_.push(std::move(obj));
}
};
实测数据显示,在每秒创建10000+对象的场景下,对象池可以减少85%的内存分配开销。但需要注意对象状态重置问题,避免脏数据影响业务逻辑。
4.2 工厂模式的内存管理
现代C++工厂应该优先返回智能指针:
cpp复制std::unique_ptr<Product> Factory::create() {
return std::make_unique<ConcreteProduct>();
}
在需要共享所有权的场景下,可以考虑shared_ptr,但要警惕循环引用。我的经验法则是:工厂内部使用unique_ptr,仅在必须共享时通过std::move转换为shared_ptr。
5. 典型问题排查指南
5.1 类型注册失败排查
当遇到"Type not registered"异常时,检查:
- 注册代码是否确实执行(在静态初始化阶段注册可能不可靠)
- 类型是否被RTTI识别(动态库边界可能导致typeid不一致)
- 模板特化是否正确定义
5.2 多线程下的竞态条件
常见症状包括:
- 偶尔抛出"Key not found"异常
- 内存访问越界崩溃
- 对象状态异常
解决方案:
- 使用读写锁(std::shared_mutex)替代互斥锁
- 采用copy-on-write策略更新工厂注册表
- 对于配置型工厂,考虑完全不可变设计
5.3 跨DLL边界问题
在Windows平台动态库中使用工厂时:
cpp复制// 显式导出符号
#define API __declspec(dllexport)
class API Product {
public:
virtual ~Product() = default;
virtual void operation() = 0;
};
// 工厂方法也需要导出
extern "C" API Product* createProduct();
关键点:
- 保持虚函数表布局一致(使用相同编译器版本)
- 提供明确的销毁接口
- 考虑使用COM接口作为跨模块边界方案
6. 设计模式组合实践
6.1 工厂与策略模式融合
在游戏AI系统中,可以这样组合:
cpp复制class BehaviorFactory {
using StrategyCreator = std::function<std::unique_ptr<BehaviorStrategy>()>;
std::map<BehaviorType, StrategyCreator> creators_;
public:
std::unique_ptr<AIUnit> createAI(BehaviorType type) {
auto strategy = creators_.at(type)();
return std::make_unique<AIUnit>(std::move(strategy));
}
};
这种设计使得AI行为策略可以独立变化,我在MMO服务器开发中应用此模式,实现了热更新战斗AI而无需重启服务。
6.2 原型模式增强工厂
当对象构造成本很高时:
cpp复制class PrototypeFactory {
std::unordered_map<std::string, std::unique_ptr<Product>> prototypes_;
public:
void registerPrototype(const std::string& id, std::unique_ptr<Product> proto) {
prototypes_.emplace(id, std::move(proto));
}
std::unique_ptr<Product> create(const std::string& id) {
return prototypes_.at(id)->clone();
}
};
实测在3D建模软件中,使用原型模式创建相似模型比直接构造快40倍。关键在于实现正确的深拷贝方法。
7. C++20/23新特性展望
概念(Concepts)将改变工厂设计:
cpp复制template <Product T>
concept ProductType = requires {
{ T::create() } -> std::convertible_to<std::unique_ptr<Product>>;
};
template <ProductType T>
auto create() {
return T::create();
}
模块(Modules)则可能影响工厂的注册方式,我们正在测试的编译期反射提案更是可能彻底革新工厂模式的实现方式。目前可以通过第三方库如meta来实现类似效果。