1. 工厂模式基础与核心价值
工厂模式是面向对象设计中应用最广泛的创建型模式之一,其核心价值在于将对象的创建与使用解耦。在C++这种强类型语言中,工厂模式能有效解决以下痛点:
- 类型强耦合问题:当代码中直接使用
new创建对象时,客户端代码必须知道具体类名,导致调用方与实现类产生编译期依赖 - 复杂初始化场景:某些对象的创建需要多步初始化或依赖外部配置,这些逻辑散落在代码各处会降低可维护性
- 多态创建需求:当需要根据运行时条件决定创建哪种具体对象时,简单
new操作无法满足要求
经典工厂模式通常包含三个角色:
- 抽象产品(Product):定义产品接口
- 具体产品(ConcreteProduct):实现产品接口的具体类
- 工厂类(Factory):负责创建产品对象的类
cpp复制// 抽象产品
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
// 具体产品
class WindowsButton : public Button {
public:
void render() override { /* Windows风格渲染 */ }
};
// 工厂基类
class ButtonFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual ~ButtonFactory() = default;
};
// 具体工厂
class WindowsButtonFactory : public ButtonFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
};
2. 静态工厂方法实现
静态工厂方法是工厂模式最简单的变体,它通过类中的静态方法封装对象创建逻辑。这种实现方式不需要创建单独的工厂类,适合简单场景:
cpp复制class Button {
public:
enum class Type { Windows, Mac };
static std::unique_ptr<Button> create(Type type) {
switch(type) {
case Type::Windows: return std::make_unique<WindowsButton>();
case Type::Mac: return std::make_unique<MacButton>();
default: throw std::invalid_argument("Unknown button type");
}
}
virtual void render() = 0;
virtual ~Button() = default;
};
优势分析:
- 实现简单,减少类爆炸问题
- 调用方只需知道
Button类和Type枚举 - 创建逻辑集中在一处,便于修改
典型应用场景:
- 对象创建逻辑简单,不需要复杂初始化
- 产品类型有限且相对稳定
- 希望避免引入额外的工厂类
注意:当产品类型频繁增加时,静态工厂方法会导致
create函数不断修改,违反开闭原则。此时应考虑其他变体。
3. 模板工厂模式实现
模板工厂利用C++模板元编程特性,在编译期生成具体的工厂类,这种技术常见于现代C++框架中:
cpp复制template <typename ProductType>
class GenericFactory {
public:
template <typename... Args>
std::unique_ptr<ProductType> create(Args&&... args) {
return std::make_unique<ProductType>(std::forward<Args>(args)...);
}
};
// 使用示例
class CustomButton : public Button {
public:
CustomButton(int size, Color color) {...}
void render() override {...}
};
GenericFactory<CustomButton> factory;
auto button = factory.create(30, Color::Blue);
技术要点:
- 可变参数模板支持任意构造参数
std::forward完美转发保持参数的值类别- 工厂类与产品类完全解耦
性能优势:
- 零运行时开销,所有逻辑在编译期确定
- 内联优化空间大
- 无虚函数调用开销
适用场景:
- 产品构造参数可能变化
- 需要极致性能的场合
- 工厂逻辑高度统一的情况
4. 多态工厂与注册机制
对于需要动态扩展的场景,可采用工厂注册机制。这是插件式架构的常见实现方式:
cpp复制class ButtonFactory {
public:
using Creator = std::function<std::unique_ptr<Button>()>;
static ButtonFactory& instance() {
static ButtonFactory inst;
return inst;
}
void registerCreator(const std::string& type, Creator creator) {
creators_[type] = creator;
}
std::unique_ptr<Button> create(const std::string& type) {
auto it = creators_.find(type);
if(it != creators_.end()) {
return it->second();
}
throw std::runtime_error("Unknown button type: " + type);
}
private:
std::unordered_map<std::string, Creator> creators_;
};
// 注册示例
struct WindowsButtonRegistrar {
WindowsButtonRegistrar() {
ButtonFactory::instance().registerCreator(
"windows",
[] { return std::make_unique<WindowsButton>(); }
);
}
} windowsRegistrar;
关键技术点:
- 单例模式管理工厂实例
std::function存储创建函数- 自动注册器模式(Registrar)
- 线程安全考虑(本例依赖C++11的magic static)
扩展性优势:
- 新增产品类型不需修改工厂类
- 支持运行时动态加载新类型
- 适合插件架构开发
实际应用技巧:
- 可结合动态库实现热插拔
- 注册时可添加优先级参数控制创建顺序
- 可扩展为依赖注入容器
5. 抽象工厂模式进阶
当需要创建产品族(相关或依赖的对象集合)时,抽象工厂模式比简单工厂更合适。以UI系统为例:
cpp复制// 抽象产品接口
class Widget {
public:
virtual void draw() = 0;
virtual ~Widget() = default;
};
class Button : public Widget {...};
class ScrollBar : public Widget {...};
// 抽象工厂
class UIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<ScrollBar> createScrollBar() = 0;
virtual ~UIFactory() = default;
};
// 具体工厂
class WindowsUIFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<ScrollBar> createScrollBar() override {
return std::make_unique<WindowsScrollBar>();
}
};
模式特点:
- 强调产品之间的兼容性
- 一个工厂类创建多个相关产品
- 新增产品族容易,新增产品种类困难
设计考量:
- 产品接口设计应保持稳定
- 工厂接口不宜包含太多创建方法
- 可结合原型模式减少子类数量
性能优化技巧:
- 工厂对象通常可复用,建议做成单例
- 频繁创建的对象可使用对象池
- 轻量级对象考虑返回值而非指针
6. 工厂模式在现代C++中的演进
C++17/20引入的新特性为工厂模式带来了更多可能性:
使用std::variant实现类型安全创建:
cpp复制using ButtonVariant = std::variant<WindowsButton, MacButton, LinuxButton>;
template <typename T>
ButtonVariant createButton() {
if constexpr (std::is_same_v<T, WindowsButton>) {
return WindowsButton{};
}
// 其他类型判断...
}
// 使用示例
auto button = createButton<WindowsButton>();
concept约束的工厂接口:
cpp复制template <typename T>
concept ButtonType = requires {
std::is_base_of_v<Button, T>;
T::render();
};
template <ButtonType T>
class TypedFactory {
public:
auto create() { return std::make_unique<T>(); }
};
协程工厂(C++20):
cpp复制Generator<std::unique_ptr<Button>> buttonGenerator() {
co_yield std::make_unique<WindowsButton>();
co_yield std::make_unique<MacButton>();
// ...
}
现代C++最佳实践:
- 优先使用智能指针管理对象生命周期
- 考虑使用
std::expected替代异常处理错误 - 对于不可拷贝对象可使用
std::unique_ptr - 线程安全场景使用
std::shared_ptr
7. 典型问题与解决方案
问题1:循环依赖
当工厂与产品相互引用时会导致编译错误。解决方案:
- 前向声明产品类
- 将工厂方法实现移到cpp文件
- 使用接口隔离
问题2:性能瓶颈
虚函数调用和动态分配可能影响性能。优化方案:
- 使用模板减少运行时多态
- 对象池复用实例
- 考虑静态多态(CRTP)
问题3:扩展困难
传统工厂添加新产品需修改代码。改进方案:
- 注册机制+配置文件
- 插件架构
- 元编程自动注册
常见反模式:
- 上帝工厂:单个工厂创建所有类型对象
- 过度抽象:为简单对象引入不必要的工厂
- 忽略生命周期管理:裸指针容易导致内存泄漏
调试技巧:
- 在工厂方法中添加日志输出
- 使用自定义删除器跟踪对象销毁
- 对工厂返回的指针进行包装调试
8. 工程实践建议
-
代码组织规范:
- 工厂接口单独放在
factories目录 - 产品实现放在
products子目录 - 每个具体工厂对应一个头文件和实现文件
- 工厂接口单独放在
-
测试策略:
cpp复制TEST(ButtonFactoryTest, CreateWindowsButton) { auto factory = WindowsButtonFactory{}; auto button = factory.createButton(); EXPECT_NE(button, nullptr); EXPECT_TRUE(dynamic_cast<WindowsButton*>(button.get())); } -
性能考量指标:
- 对象创建延迟(纳秒级)
- 内存分配次数
- 缓存命中率
- 线程安全开销
-
设计决策流程图:
code复制是否需要创建多种类型对象? ├─ 是 → 对象创建逻辑是否复杂? │ ├─ 是 → 使用标准工厂模式 │ └─ 否 → 使用静态工厂方法 └─ 否 → 是否需要产品族支持? ├─ 是 → 使用抽象工厂 └─ 否 → 直接构造对象即可 -
工具支持:
- CLion:工厂方法重构支持
- Doxygen:特殊注释生成文档
- SonarQube:检测工厂模式滥用
在大型C++项目中,我通常会采用混合策略:核心模块使用模板工厂保证性能,插件系统使用注册机制保证扩展性,UI等可变部分采用抽象工厂。对于有明确生命周期管理的对象,工厂方法应返回std::unique_ptr而非裸指针,这能显著减少内存泄漏问题。
