工厂方法模式(Factory Method):把"创建谁"交给子类
更新时间:2026-08-25。本文按"先看不用模式的困境 → 再用模式重构 → 类图对比分析"的结构,论述工厂方法模式。可运行代码:demos/design-patterns/factory-method。
一、问题场景:创建对象的代码不该写死在调用方
解析器要根据文件后缀选解析器、日志器要根据配置选输出端、UI 要根据主题选控件。共同点:调用方知道"要用产品",却不想知道"具体是哪个产品、怎么建出来"。
二、不使用模式:直接 new + if/else
cpp
// 根据后缀创建解析器(调用方直接 new)
Parser* createParser(const std::string& ext) {
if (ext == "json") return new JsonParser();
if (ext == "xml") return new XmlParser();
if (ext == "yaml") return new YamlParser(); // 加新格式:改这里
return nullptr;
}| 缺陷 | 说明 |
|---|---|
| 违反 OCP | 每加一种产品,createParser 的分支链就要改一次并全量回归 |
| 违反 DIP | 调用方直接依赖所有具体 *Parser 类 |
| 创建逻辑散落 | 每个调用点都写一份 if/else,创建规则重复 |
| 无法扩展创建前逻辑 | 想在创建时加日志/池化/缓存,无处安放 |
不使用模式的类图

三、使用模式:抽象工厂方法 + 子类决定产品
工厂方法把"创建逻辑"从调用方抽走:抽象创建者定义 createParser(),每个子类创建者只负责创建一种产品,调用方只依赖抽象。
cpp
// parser_factory.h
class Parser { public: virtual void parse() = 0; virtual ~Parser() = default; };
class ParserFactory { // 抽象创建者
public:
virtual std::unique_ptr<Parser> create() = 0;
virtual ~ParserFactory() = default;
};
class JsonFactory : public ParserFactory {
public:
std::unique_ptr<Parser> create() override { return std::make_unique<JsonParser>(); }
};
class XmlFactory : public ParserFactory {
public:
std::unique_ptr<Parser> create() override { return std::make_unique<XmlParser>(); }
};
// 调用方:只依赖 ParserFactory 抽象
void client(ParserFactory& factory) {
auto p = factory.create(); // 不知道具体是哪个解析器
p->parse();
}使用模式的类图

四、类图对比与分析
| 维度 | 不使用(直接 new + 分支) | 使用工厂方法 |
|---|---|---|
| 扩展新产品 | 改 createParser 分支链 | 新增一个 *Factory + *Parser 子类 |
| 调用方依赖 | 所有具体类 | 只有抽象 ParserFactory/Parser |
| OCP 符合度 | 违反(分支要改旧代码) | 符合(纯新增) |
| 创建前逻辑(日志/池化) | 无处安放或重复写 | 收进具体工厂 |
| 灵活性 | 创建规则写死 | 创建策略可替换(换工厂对象) |
分析结论:工厂方法的本质是把"创建谁"的决策点从调用方上移到子类——调用方与产品解耦,新产品加入不再触碰旧代码。代价是多了一组 Factory 类(结构变多),适合"产品族会持续扩展"的场景。
五、适用边界
| 适用 | 不适用 |
|---|---|
| 产品类型会持续增加 | 产品类型几乎不变(工厂是多余抽象) |
| 调用方不应知道具体产品类 | 创建逻辑简单且调用方本就持有具体类 |
| 需要统一管理创建过程(池化、复用、计数) | 追求极简、产品仅一两种 |
与 Abstract Factory 的区别:工厂方法只负责一个产品的创建(一个工厂一个产品);抽象工厂负责一族相关产品的创建(一个工厂多个配套产品)。单一产品变化用工厂方法,产品族配套变化用抽象工厂。
一句话总结
工厂方法模式把"创建谁"的决策上移到子类工厂,调用方只依赖抽象产品与抽象工厂——新增产品变成纯新增(符合 OCP),创建前逻辑有了统一安放点;当产品类型单一且稳定时,它比多一层工厂更划算。