工厂方法模式的核心是基类声明纯虚工厂函数,派生类实现以返回具体对象;必须用虚函数而非模板,返回智能指针,支持运行时多态与扩展,避免基类修改。

工厂方法模式的核心是让子类决定实例化哪个类
它不是简单地把 new 拆开写,而是把对象创建逻辑从使用者中抽离,交给专门的“工厂”类或虚函数去处理。关键在于:**基类声明一个纯虚工厂函数,派生类各自实现它来返回具体类型对象**。
常见错误是直接在基类里写 new ConcreteProduct() —— 这就退化成简单工厂,违背了“开放-封闭”原则,每加一种产品就得改基类。
- 工厂方法必须是虚函数,且通常返回基类指针(
std::unique_ptr<product></product>或Product*) - 不要返回栈对象或裸指针(易悬挂、难管理),优先用
std::unique_ptr - 工厂类本身可以是抽象类,也可以是普通类,但它的创建接口必须可被重载
用虚函数实现工厂方法比用模板更符合设计意图
有人会用模板函数(如 create<concretea>()</concretea>)替代虚函数,但这属于编译期多态,无法在运行时根据配置/输入动态选择类型,也失去了“子类决定创建谁”的语义。
真正体现工厂方法价值的场景是:你有一组业务策略(PaymentStrategy),运行时读取配置决定用 AlipayStrategy 还是 WechatStrategy,然后调用对应的工厂方法生成实例。
- 模板方案适合类型已知、无运行时分支的场景,不属于 GoF 工厂方法模式
- 虚函数方案支持多态容器(如
std::vector<:unique_ptr>></:unique_ptr>)统一管理不同子类对象 - 注意 vtable 开销极小,别为这点性能放弃可扩展性
容易漏掉的 RAII 和异常安全细节
工厂方法返回智能指针时,构造失败(比如 new 抛 std::bad_alloc)会导致资源泄漏风险——尤其当工厂内部还做了其他初始化操作(如打开文件、连接网络)。
正确做法是:所有资源获取都放在构造函数内,或使用“两阶段构造 + 工厂检查”,但更推荐前者。
- 每个具体产品的构造函数应尽可能做最小初始化;重逻辑(如加载配置)移到独立的
init()方法中 - 工厂方法体内不要捕获异常并吞掉——调用方需要知道创建失败
- 如果工厂需传参(如配置字符串),确保参数能被所有子类消费,或用
std::any/std::variant包装,但会增加耦合
一个最小可行示例:只含必要骨架
class Product {
public:
virtual ~Product() = default;
virtual void operation() = 0;
};
<p>class ConcreteProductA : public Product {
public:
void operation() override { /<em> ... </em>/ }
};</p><p>class Creator {
public:
std::unique_ptr<product> create() {
return factory_method(); // 委托给子类
}
protected:
virtual std::unique_ptr<product> factory_method() = 0;
};</product></product></p><p>class ConcreteCreatorA : public Creator {
protected:
std::unique_ptr<product> factory_method() override {
return std::make_unique<concreteproducta>();
}
};</concreteproducta></product></p>
调用时:auto p = ConcreteCreatorA{}.create(); —— 创建逻辑和使用完全解耦。后续加 ConcreteProductB 和 ConcreteCreatorB,无需动已有代码。
真正麻烦的从来不是写这个模式,而是判断该不该用:如果只有两三个固定类型、永不扩展,硬套工厂方法反而增加理解成本。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











