一、模式简介

工厂方法模式(Factory Method Pattern)是创建型设计模式的核心成员,其核心思想是:定义一个创建对象的接口(抽象工厂),但由子类(具体工厂)决定实例化哪个类(具体产品)。通过这种设计,将对象的创建逻辑与使用逻辑彻底分离,让代码更具扩展性和可维护性。

二、核心概念与结构

工厂方法模式包含 4 个关键角色,形成清晰的继承体系:

抽象产品(Abstract Product)

定义产品的通用接口,所有具体产品都需实现该接口。例如 “交通工具” 抽象类,包含 “行驶” 纯虚函数。

具体产品(Concrete Product)

抽象产品的实现类,是工厂方法最终创建的对象。例如 “汽车”“自行车” 类,分别实现 “行驶” 方法。

抽象工厂(Abstract Factory)

定义创建产品的接口(工厂方法),返回抽象产品类型。例如 “交通工具工厂” 抽象类,包含 “创建交通工具” 纯虚函数。

具体工厂(Concrete Factory)

抽象工厂的实现类,重写工厂方法,返回具体产品实例。例如 “汽车工厂” 创建汽车对象,“自行车工厂” 创建自行车对象。

三、代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>

// 1. 抽象产品:交通工具
class Vehicle {
public:
virtual void run() = 0; // 纯虚函数,定义产品接口
virtual ~Vehicle() {} // 虚析构,确保子类析构正常
};

// 2. 具体产品:汽车
class Car : public Vehicle {
public:
void run() override { std::cout << "汽车:百公里加速5秒\n"; }
};

// 3. 抽象工厂:交通工具工厂
class VehicleFactory {
public:
virtual Vehicle* createVehicle() = 0; // 工厂方法
virtual ~VehicleFactory() {}
};

// 4. 具体工厂:汽车工厂
class CarFactory : public VehicleFactory {
public:
Vehicle* createVehicle() override { return new Car(); } // 创建具体产品
};

// 使用示例
int main() {
VehicleFactory* factory = new CarFactory(); // 选择具体工厂
Vehicle* vehicle = factory->createVehicle(); // 工厂创建产品
vehicle->run(); // 调用产品方法(多态特性)

delete vehicle;
delete factory;
return 0;
}

输出结果:汽车:百公里加速5秒

四、与简单工厂模式的对比

简单工厂模式(Simple Factory Pattern)虽然不属于 GoF 23 种设计模式,但也是常用的对象创建方案。它通过一个工厂类的静态方法,根据传入参数决定创建哪个具体产品。两种模式的主要差异如下:

对比维度 工厂方法模式 简单工厂模式
核心设计 通过抽象工厂类定义创建接口,由子类实现具体创建逻辑 单一工厂类包含所有创建逻辑,使用条件语句(如 if-else)选择产品
扩展性 符合 “开闭原则”,新增产品只需添加具体产品和工厂类 新增产品需修改工厂类的创建逻辑(添加新的条件分支),违背 “开闭原则”
灵活性 支持多态调用,同一调用逻辑可适配不同具体工厂 调用者需传入具体产品标识,无法实现多态切换产品类型
代码复杂度 类数量较多,适合复杂、需频繁扩展的场景 代码简洁,适合产品类型固定、创建逻辑简单的场景
应用场景 框架设计、插件系统等需要高度扩展性的场景 业务初期快速开发,或产品类型较少且稳定的场景

例如,在日志系统中,若未来可能扩展 “文件日志”“控制台日志”“网络日志” 等多种类型,工厂方法模式更合适;若仅需区分 “普通日志” 和 “错误日志” 两种固定类型,简单工厂模式能以更少代码量实现。

五、应用场景

当代码满足以下条件时,优先使用工厂方法模式:

不确定具体产品类型:例如框架开发中,无法预知用户会扩展哪些产品(如插件系统)。

需要统一创建逻辑:多个产品的创建有共性流程(如初始化配置、权限校验),可在抽象工厂中封装。

希望解耦创建与使用:调用者只需知道抽象产品接口,无需关心具体实现(如日志系统,无需区分 “文件日志” 还是 “控制台日志”)。

六、优缺点分析

优点 缺点
1. 符合 “开闭原则”:新增产品时,只需添加具体产品和具体工厂,无需修改原有代码 1. 类数量增加:每新增一个产品,需对应新增一个具体工厂类,增加代码复杂度
2. 依赖抽象而非具体:调用者依赖抽象产品和工厂,降低代码耦合度 2. 理解成本提升:相比直接 new 对象,需要理解抽象与子类的继承关系
3. 利用多态特性:同一调用逻辑可适配不同产品,代码更灵活 3. 简单场景冗余:若产品类型固定且无需扩展,使用工厂方法会显得冗余