【西瓜带你学设计模式 | 第三期-工厂方法模式】工厂方法模式——定义、实现方式、优缺点与适用场景以及注意事项

文章目录

    • 前言
    • [1. 工厂方法模式是什么?](#1. 工厂方法模式是什么?)
    • [2. 它解决什么问题?](#2. 它解决什么问题?)
    • [3. 核心结构](#3. 核心结构)
    • [4. 示例](#4. 示例)
      • [4.1 抽象产品:支付接口](#4.1 抽象产品:支付接口)
      • [4.2 具体产品:AliPay、WeChatPay](#4.2 具体产品:AliPay、WeChatPay)
      • [4.3 抽象创建者:定义工厂方法](#4.3 抽象创建者:定义工厂方法)
      • [4.4 具体创建者:决定创建哪种产品](#4.4 具体创建者:决定创建哪种产品)
      • [4.5 使用方:只依赖抽象创建者](#4.5 使用方:只依赖抽象创建者)
    • [5. "工厂 + 延迟决策"](#5. “工厂 + 延迟决策”)
    • [6. 优缺点](#6. 优缺点)
    • [7. 适用场景总结](#7. 适用场景总结)
    • [8. 与"简单工厂 / 抽象工厂"的区别](#8. 与“简单工厂 / 抽象工厂”的区别)
      • [8.1 简单工厂(Simple Factory)](#8.1 简单工厂(Simple Factory))
      • [8.2 工厂方法(Factory Method)](#8.2 工厂方法(Factory Method))
      • [8.3 抽象工厂(Abstract Factory)](#8.3 抽象工厂(Abstract Factory))
    • [9. 总结](#9. 总结)

前言

工厂方法模式(Factory Method)"里,我们关注的核心不是"怎么创建对象",而是:

把"对象的创建"延迟到子类去决定,从而让创建逻辑和使用逻辑解耦。

当你发现代码里到处是 new XXX()(而且具体实现会不断增加),就应该考虑用工厂方法模式把这种变化"隔离"起来。


1. 工厂方法模式是什么?

工厂方法模式 :定义一个创建对象的接口/抽象方法,但由子类决定要实例化哪一个类

也就是说:父类负责"定义创建过程",子类负责"决定创建哪个产品"。


2. 它解决什么问题?

你可能写出了这种"违背开闭原则"的代码:

java 复制代码
public class PaymentService {
    public void pay(String channel) {
        if ("ALI".equals(channel)) {
            new AliPay().pay();
        } else if ("WECHAT".equals(channel)) {
            new WeChatPay().pay();
        }
    }
}

当未来新增"银联支付""Apple Pay"等,你就得不停改 PaymentService

这会导致:

  • 扩展需要修改已有核心代码
  • 创建逻辑混杂在业务逻辑里
  • 可维护性下降

工厂方法模式的目标就是:

  • 业务方只关心"通过工厂创建产品并调用"
  • 新增产品时,只需要新增子类/工厂对应实现,尽量不改原有代码

3. 核心结构

工厂方法模式通常包含这几块:

  1. 抽象产品(Product):产品的公共接口/抽象类
  2. 具体产品(ConcreteProduct):产品实现类
  3. 抽象创建者(Creator) :声明工厂方法 factoryMethod()
  4. 具体创建者(ConcreteCreator) :实现 factoryMethod(),决定创建哪个具体产品

用一句话串起来:

Creator 里有"创建产品"的方法声明,但真正 new 哪个产品由子类来决定。


4. 示例

下面用"支付方式选择"来演示

4.1 抽象产品:支付接口

java 复制代码
public interface Pay {
    void pay();
}

4.2 具体产品:AliPay、WeChatPay

java 复制代码
public class AliPay implements Pay {
    @Override
    public void pay() {
        System.out.println("AliPay paying...");
    }
}

public class WeChatPay implements Pay {
    @Override
    public void pay() {
        System.out.println("WeChatPay paying...");
    }
}

4.3 抽象创建者:定义工厂方法

java 复制代码
public abstract class PayCreator {

    // 工厂方法:由子类决定创建哪个具体产品
    protected abstract Pay factoryMethod();

    // 业务方法:父类固定流程,复用这套创建+调用逻辑
    public void processPay() {
        Pay pay = factoryMethod();
        pay.pay();
    }
}

4.4 具体创建者:决定创建哪种产品

java 复制代码
public class AliPayCreator extends PayCreator {
    @Override
    protected Pay factoryMethod() {
        return new AliPay();
    }
}

public class WeChatPayCreator extends PayCreator {
    @Override
    protected Pay factoryMethod() {
        return new WeChatPay();
    }
}

4.5 使用方:只依赖抽象创建者

java 复制代码
public class Main {
    public static void main(String[] args) {
        PayCreator creator = new AliPayCreator();
        creator.processPay();
    }
}

5. "工厂 + 延迟决策"

你可能注意到:

  • 业务流程 processPay() 放在抽象创建者里固定了
  • "到底 new 哪个产品"交给子类实现 factoryMethod()

所以它是典型的:

  • 把"创建变化点"抽象出来
  • 把"使用固定流程"保留下来

6. 优缺点

优点

  1. 符合开闭原则
    • 新增产品:新增"具体产品 + 对应具体创建者"
    • 尽量不改动原有业务类
  2. 创建逻辑解耦
    • processPay() 不关心具体 new AliPay() 还是 new WeChatPay()
  3. 更易扩展
    • 当产品族不断增长,代码结构更清晰

缺点

  1. 类会变多
    • 每一种产品通常要对应一个具体创建者(也就是更多类)
  2. 如果只是简单 switch/if
    • 工厂方法的引入可能有"过度设计"风险

7. 适用场景总结

建议在以下情况使用:

  • 系统需要创建的对象类型不断增加
  • 创建逻辑需要与业务逻辑解耦
  • 希望"扩展一个产品"时,不要动核心业务流程
  • 希望将 new 操作从业务代码中抽离出来

8. 与"简单工厂 / 抽象工厂"的区别

8.1 简单工厂(Simple Factory)

  • 通常只有一个工厂类,根据参数 switch/if 返回不同产品
  • 缺点:工厂类会膨胀,新增类型需要改工厂类

8.2 工厂方法(Factory Method)

  • 每个"产品类型"对应自己的"创建者子类"
  • 新增产品:新增子类,不一定改已有创建者(更符合开闭)

8.3 抽象工厂(Abstract Factory)

  • 工厂方法解决的是:创建一个产品层级
  • 抽象工厂解决的是:创建一整套产品族(多个相关产品要一起创建,并保持兼容性)
  • 例如:不同 UI 主题(按钮/输入框/菜单)要成套创建

一句话区分:

  • 工厂方法:一个产品维度的创建延迟
  • 抽象工厂:多个产品维度的成套创建保证一致

9. 总结

工厂方法模式通过"抽象创建者 + 延迟到子类决定具体产品",把创建变化和使用逻辑解耦,从而更容易扩展、更符合开闭原则。

相关推荐
敖正炀2 小时前
Java 线程状态变化与ObjectMonitor之间的关系
jvm·后端
前端付豪2 小时前
Prompt Playground(实现提示词工作台)
前端·人工智能·后端
Zzxy2 小时前
Spring Security + JWT 简单集成
java·spring boot
2401_827499992 小时前
python核心语法01-数据存储与运算
java·数据结构·python
谁在黄金彼岸2 小时前
MariaDB Docker容器权限配置问题分析与解决方案
后端·docker·容器
镜花水月linyi2 小时前
Redis 为什么快?
redis·后端
Magic--2 小时前
进程间通信(IPC):原理、场景与选型
java·服务器·数据库
Walter先生2 小时前
实时行情系统设计:从协议选择到高可用架构,再到数据源选型
后端·架构·实时行情数据源
老毛肚2 小时前
黑马头条 云岚到家
java