第 2 天:工厂方法模式(Factory Method Pattern)------ 创建型模式
1. 核心定义
工厂方法模式定义一个创建对象的接口 (抽象工厂),但由子类决定实例化哪个类。它将对象的创建逻辑延迟到子类中,实现了 "创建责任" 与 "使用责任" 的彻底分离。
简单来说:父类只规定 "要创建什么类型的对象",具体创建 "哪个具体对象" 交给子类来完成。
2. 解决的核心问题
在学习工厂方法前,我们先理解它要解决的痛点 ------简单工厂模式的局限性:
- 简单工厂模式通过一个 "万能工厂类" 创建所有对象,若新增产品(如从 "手机" 新增 "平板"),必须修改工厂类的逻辑(添加
if-else
或switch
),违反开闭原则(对扩展开放、对修改关闭); - 工厂类职责过重,所有产品的创建逻辑集中在一个类中,代码臃肿且不易维护。
工厂方法模式的解决方案:
- 将 "万能工厂" 拆分为 "抽象工厂 + 多个具体工厂",每个具体工厂只负责创建一种具体产品;
- 新增产品时,只需新增 "具体产品类 + 对应的具体工厂类",无需修改原有代码,完全符合开闭原则。
3. 核心角色(4 个)
工厂方法模式的结构清晰,包含 4 个固定角色,缺一不可:
角色名称 | 核心职责 | 示例(以 "电子设备生产" 为例) |
---|---|---|
抽象产品(Product) | 定义所有具体产品的公共接口 / 抽象类,规范产品的功能 | 抽象类 ElectronicDevice ,包含抽象方法 powerOn() (开机) |
具体产品(Concrete Product) | 实现抽象产品的接口,是工厂方法模式的创建目标 | 类 Phone (手机)、Tablet (平板),均实现 powerOn() |
抽象工厂(Factory) | 定义创建具体产品的接口(含抽象方法 createProduct() ),声明返回抽象产品类型 |
接口 DeviceFactory ,含方法 createElectronicDevice() |
具体工厂(Concrete Factory) | 实现抽象工厂的接口,重写 createProduct() 方法,返回具体产品实例 |
类 PhoneFactory (创建 Phone )、TabletFactory (创建 Tablet ) |
4. 实现步骤与代码示例(Java)
以 "电子设备生产" 为例,完整实现工厂方法模式:
步骤 1:定义抽象产品(ElectronicDevice)
规范所有电子设备的公共行为(如开机):
java
// 抽象产品:电子设备
public abstract class ElectronicDevice {
// 抽象方法:开机
public abstract void powerOn();
}
步骤 2:实现具体产品(Phone、Tablet)
每个具体产品对应一种实际设备,实现抽象方法:
java
// 具体产品1:手机
public class Phone extends ElectronicDevice {
@Override
public void powerOn() {
System.out.println("手机开机:显示品牌Logo,进入主屏幕");
}
}
// 具体产品2:平板
public class Tablet extends ElectronicDevice {
@Override
public void powerOn() {
System.out.println("平板开机:自动连接WiFi,显示分屏界面");
}
}
步骤 3:定义抽象工厂(DeviceFactory)
声明创建产品的接口,返回抽象产品类型(依赖抽象而非具体):
csharp
// 抽象工厂:电子设备工厂
public interface DeviceFactory {
// 抽象方法:创建电子设备(返回抽象产品类型)
ElectronicDevice createElectronicDevice();
}
步骤 4:实现具体工厂(PhoneFactory、TabletFactory)
每个具体工厂对应一种具体产品,负责创建实例:
java
// 具体工厂1:手机工厂
public class PhoneFactory implements DeviceFactory {
@Override
public ElectronicDevice createElectronicDevice() {
// 只负责创建Phone实例
return new Phone();
}
}
// 具体工厂2:平板工厂
public class TabletFactory implements DeviceFactory {
@Override
public ElectronicDevice createElectronicDevice() {
// 只负责创建Tablet实例
return new Tablet();
}
}
步骤 5:客户端使用(创建与使用分离)
客户端只需依赖 "抽象工厂" 和 "抽象产品",无需知道具体实现,实现解耦:
java
public class Client {
public static void main(String[] args) {
// 1. 选择具体工厂(如需要手机,就用PhoneFactory)
DeviceFactory factory = new PhoneFactory();
// DeviceFactory factory = new TabletFactory(); // 切换为平板工厂,只需改这一行
// 2. 通过工厂创建产品(返回抽象产品类型,无需关注具体是Phone还是Tablet)
ElectronicDevice device = factory.createElectronicDevice();
// 3. 使用产品(调用抽象方法,行为由具体产品决定)
device.powerOn();
}
}
运行结果(PhoneFactory 时) :
plaintext
手机开机:显示品牌Logo,进入主屏幕
运行结果(TabletFactory 时) :
plaintext
平板开机:自动连接WiFi,显示分屏界面
5. 应用场景
当满足以下任一条件时,优先使用工厂方法模式:
- 产品种类可能扩展:如系统需支持多种支付方式(微信支付、支付宝支付)、多种日志输出(文件日志、控制台日志),后续可能新增更多类型;
- 创建逻辑复杂:如创建对象前需初始化配置、连接资源(如数据库驱动创建),需将复杂逻辑封装在具体工厂中;
- 客户端无需关注创建细节 :如框架开发中,用户只需调用 API 获取对象,无需知道对象的创建过程(如 Spring 中的
BeanFactory
就是工厂方法的典型应用)。
经典实战案例:
- Java 中的
Collection
接口:List
、Set
是抽象产品,ArrayList
、HashSet
是具体产品;Collection
的工厂方法(如Arrays.asList()
)可视为简化的工厂实现; - 日志框架 Logback/Log4j:
Logger
是抽象产品,ConsoleAppenderLogger
、FileAppenderLogger
是具体产品,LoggerFactory
是抽象工厂的入口。
6. 优缺点分析
优点 | 缺点 |
---|---|
1. 符合开闭原则:新增产品只需加 "具体产品 + 具体工厂",无需改原有代码;2. 解耦创建与使用:客户端只依赖抽象,不依赖具体实现,代码更灵活;3. 单一职责:每个具体工厂只负责创建一种产品,逻辑清晰。 | 1. 类数量增加:每新增一个产品,需对应新增一个具体工厂,可能导致类膨胀;2. 学习成本:相比简单工厂,需理解 "抽象工厂 + 抽象产品" 的多层结构,对新手不友好。 |
7. 与简单工厂模式的核心区别
很多人会混淆 "简单工厂" 和 "工厂方法",这里用一句话区分:
- 简单工厂 :1 个工厂 → 生产 N 种产品(用
if-else
判断),违反开闭原则; - 工厂方法:N 个工厂 → 生产 N 种产品(1 个工厂对应 1 种产品),符合开闭原则。
简单工厂是 "工厂方法的简化版",适合产品种类固定、不会频繁扩展的场景(如 JDK 中的 Calendar.getInstance()
);工厂方法则适合产品种类可能扩展的场景(如业务系统中的支付、登录方式)。