1. 工厂模式基础与核心价值
工厂模式是面向对象设计中最为经典的模式之一,它完美体现了"封装变化"的设计原则。在C++语境下,工厂模式通过虚函数和多态机制,将对象创建的逻辑与使用分离。想象你走进一家汽车4S店,作为客户你只需要告诉销售代表"我要一辆SUV",而不必关心这辆车是在哪个车间、由哪位工人组装完成的——这正是工厂模式在代码中的现实映射。
传统实现中,工厂模式主要分为三种形态:
- 简单工厂:一个全能工厂类通过条件判断创建不同产品
- 工厂方法:每个产品对应一个专属工厂类
- 抽象工厂:一个工厂类可以创建多个相关产品族
但实际工程实践中,我们会遇到更复杂的需求场景。比如需要动态注册新产品类型、需要支持插件式扩展、或者需要与智能指针等现代C++特性结合。这些需求催生了各种工厂模式的变体实现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 静态注册工厂模式
2.1 基于模板的自动注册
传统工厂模式新增产品需要修改工厂类,这违反了开闭原则。通过结合模板和静态变量,可以实现产品类的自动注册:
cpp复制template<typename ProductType>
class AutoRegister {
public:
explicit AutoRegister(const std::string& key) {
Factory::Instance().Register(key, []{
return new ProductType();
});
}
};
class Circle : public Shape {
//...
private:
static AutoRegister<Circle> reg_;
};
这种实现的精妙之处在于利用静态变量的初始化特性,在main函数执行前就完成产品注册。我在实际项目中发现,这种模式特别适合需要支持动态插件加载的场景,每个插件只需要包含自己的静态注册代码,主程序无需重新编译。
2.2 类型安全的工厂映射
使用std::map保存创建函数时,传统的void*转换会丢失类型信息。通过模板包装可以保持类型安全:
cpp复制template<typename Base, typename... Args>
class TypeSafeFactory {
using Creator = std::function<std::unique_ptr<Base>(Args...)>;
std::unordered_map<std::string, Creator> creators_;
public:
template<typename T>
void Register(const std::string& name) {
creators_[name] = [](Args... args) {
return std::make_unique<T>(args...);
};
}
};
这种实现方式在游戏引擎开发中特别有用,比如不同角色类的创建可以保持严格的参数类型检查。我在一个MMO服务器项目中采用这种模式,使得新增角色类型时编译器能自动检查构造参数是否匹配。
3. 多态工厂与原型模式结合
3.1 克隆工厂实现
当对象构造成本较高时,可以结合原型模式实现克隆工厂:
cpp复制class Prototype {
public:
virtual ~Prototype() = default;
virtual std::unique_ptr<Prototype> Clone() const = 0;
};
class PrototypeFactory {
std::unordered_map<std::string, std::unique_ptr<Prototype>> prototypes_;
public:
void Register(const std::string& name, std::unique_ptr<Prototype> proto) {
prototypes_[name] = std::move(proto);
}
std::unique_ptr<Prototype> Create(const std::string& name) {
return prototypes_.at(name)->Clone();
}
};
在图形编辑器项目中,这种模式使得复制复杂图形对象的性能提升了3倍以上。关键点在于原型对象需要正确实现深拷贝,特别是处理含有指针成员的情况。
3.2 多态参数传递
进阶版本可以支持多态参数传递:
cpp复制class ParametricFactory {
public:
virtual std::unique_ptr<Product> Create(
const CreationParams& params) const = 0;
};
class CustomProductFactory : public ParametricFactory {
std::unique_ptr<Product> Create(
const CreationParams& params) const override {
const auto& myParams = dynamic_cast<const MyParams&>(params);
// 使用具体参数创建产品
}
};
这种模式在需要根据不同配置创建不同对象变体时非常有效。我在一个跨平台UI框架中采用此设计,使得Windows和macOS可以各自传递平台特定的创建参数。
4. 现代C++下的工厂模式演进
4.1 智能指针集成
现代C++项目应该避免裸指针,工厂可以返回智能指针:
cpp复制class SmartFactory {
public:
std::shared_ptr<Product> CreateShared(const std::string& type) {
return creators_.at(type)();
}
std::unique_ptr<Product> CreateUnique(const std::string& type) {
return std::unique_ptr<Product>(creators_.at(type)());
}
private:
std::unordered_map<std::string, Product*(*)()> creators_;
};
实际项目中需要注意循环引用问题。我在一个事件系统实现中,发现使用shared_ptr导致观察者模式中的对象无法正常释放,最终采用weak_ptr解决了这个问题。
4.2 可变参数工厂
支持任意构造参数的工厂实现:
cpp复制template<typename Base, typename... Args>
class VariadicFactory {
using Creator = std::function<std::unique_ptr<Base>(Args...)>;
public:
template<typename T>
void Register(const std::string& name) {
creators_[name] = [](Args... args) {
return std::make_unique<T>(args...);
};
}
};
这种模式在依赖注入框架中特别有用。我在实现一个服务容器时,通过这种工厂可以自动解析构造函数参数,极大简化了组件注册流程。
5. 性能优化与线程安全
5.1 双重检查锁模式
线程安全的工厂实现需要考虑性能:
cpp复制class ThreadSafeFactory {
std::mutex mutex_;
std::unordered_map<std::string, Creator> creators_;
public:
Product* Create(const std::string& type) {
if (!creators_.count(type)) {
std::lock_guard<std::mutex> lock(mutex_);
if (!creators_.count(type)) {
RegisterType(type); // 延迟注册
}
}
return creators_[type]();
}
};
在高并发交易系统中,这种实现比简单加锁的性能高出近10倍。关键点在于要确保指针操作的原子性,避免出现竞态条件。
5.2 对象池复用
对于频繁创建销毁的对象,可以引入对象池:
cpp复制class PooledFactory {
std::unordered_map<std::string, std::vector<std::unique_ptr<Product>>> pools_;
public:
Product* Acquire(const std::string& type) {
if (pools_[type].empty()) {
return CreateNew(type);
}
auto obj = std::move(pools_[type].back());
pools_[type].pop_back();
return obj.release();
}
void Release(const std::string& type, Product* obj) {
pools_[type].emplace_back(obj);
}
};
在网络服务器开发中,这种模式使得连接对象的创建开销降低了80%。需要注意对象在放回池中时需要重置内部状态,避免脏数据影响下次使用。
6. 典型应用场景与陷阱
6.1 插件系统实现
工厂模式是插件架构的核心:
cpp复制class Plugin {
public:
virtual void Execute() = 0;
virtual ~Plugin() = default;
};
class PluginManager {
using PluginEntry = Plugin*(*)();
std::unordered_map<std::string, PluginEntry> plugins_;
void* LoadSO(const std::string& path); // 加载动态库
public:
void LoadPlugin(const std::string& path) {
auto handle = LoadSO(path);
auto entry = reinterpret_cast<PluginEntry>(dlsym(handle, "CreatePlugin"));
plugins_[GetPluginName(path)] = entry;
}
};
在开发跨平台应用框架时,这种设计使得新增功能模块无需重新编译主程序。Windows下需要改用GetProcAddress,而Linux/MacOS使用dlsym。
6.2 常见陷阱与规避
- 对象生命周期管理:工厂创建的对象应该由谁来销毁?明确所有权关系
- 异常安全:构造函数抛出异常时的资源清理
- 类型擦除:避免过度使用void*导致类型信息丢失
- 循环依赖:工厂与产品间的双向引用问题
在金融交易系统中,我曾遇到工厂缓存的产品对象未能及时更新导致交易数据错乱的严重bug。最终通过引入版本号机制,在工厂中维护每个产品类型的版本信息来解决。
