APE
文章标签归档关于

© 2026 APE.PUB

2026-08-10· 9 分钟

Factory Design Pattern

design-patternfactory

工厂模式详解

工厂模式是一种创建型设计模式:不直接用 new 构造对象,而是通过一个"工厂"方法/类来创建。根据复杂度分为简单工厂、工厂方法、抽象工厂三种。


一、它到底解决什么问题

核心矛盾:"创建谁"的逻辑和"使用谁"的逻辑纠缠在一起。

// 混乱的代码:创建逻辑散落在各个业务代码里
public class OrderService {
public void pay(Order order, String type) {
// 创建逻辑泄漏到了业务代码
if ("alipay".equals(type)) {
Payment p = new Alipay(new Config(...), new Logger(...));
} else if ("wechat".equals(type)) {
Payment p = new Wechat(new Config(...), new Logger(...));
}
p.pay(order);
}
}

问题有三:

  • 重复:每个用到支付的地方都要写一遍 if-else 和构造逻辑
  • 脆弱:新增一个支付方式,所有调用点都要改,容易漏改
  • 反向依赖:业务代码依赖了具体类(Alipay),想替换/测试都困难

工厂的解决思路:把"选谁、怎么构造"封装起来,业务代码只面向 Payment 接口。


二、三种形态(按复杂度递进)

1. 简单工厂(Simple Factory)—— 不是 GoF 23 种之一,但最常用

public class PaymentFactory {
public static Payment create(String type) {
if ("alipay".equals(type)) return new Alipay();
if ("wechat".equals(type)) return new Wechat();
throw new IllegalArgumentException("unknown: " + type);
}
}
  • 优点:最简单,够用就上
  • 缺点:工厂本身违背开闭原则 —— 加新类型还是要改 PaymentFactory
  • 适用:产品种类少且稳定、调用方多需要集中收口

2. 工厂方法(Factory Method)—— GoF 正式模式

把"创建"定义成接口,由子类决定实例化哪个类:

public abstract class PaymentFactory {
public final void execute(Order order) { // 模板方法:固定流程
Payment p = create(); // 创建延迟到子类
p.pay(order);
}
protected abstract Payment create(); // 工厂方法
}

public class AlipayFactory extends PaymentFactory {
protected Payment create() { return new Alipay(); }
}
  • 优点:新增产品 = 新增一个子类,完全符合开闭原则,且工厂方法可以和模板方法配合(创建 + 固定流程)
  • 缺点:每加一个产品就要加一个工厂类,类爆炸
  • 适用:产品族扩展频繁、创建过程需要继承复用的场景

3. 抽象工厂(Abstract Factory)—— 管理"产品族"

工厂方法管的是"一种产品",抽象工厂管的是一组配套的产品:

public interface UiFactory { // 一套风格 = 一个产品族
Button createButton();
Dialog createDialog();
}
public class DarkThemeFactory implements UiFactory { // 深色系
public Button createButton() { return new DarkButton(); }
public Dialog createDialog() { return new DarkDialog(); }
}
public class LightThemeFactory implements UiFactory { // 浅色系
public Button createButton() { return new LightButton(); }
public Dialog createDialog() { return new LightDialog(); }
}
  • 解决:产品之间必须配套使用的问题。换主题时整体换,不会出现"深色按钮 + 浅色弹窗"的混搭
  • 优点:保证一致性,符合开闭
  • 缺点:加一种新产品(如 Slider),所有工厂都要改
  • 典型:java.sql.DriverManager、Spring 的 AbstractApplicationContext

三、对工厂方法的常见疑问:选择逻辑不是还留在调用方吗?

问题:工厂方法一个类型一个子类,如果有多个类型则有多个子类,但使用的地方是不是仍然需要 if 硬判断传入哪个工厂子类?相当于把耦合逻辑提到调用方了吧?

答:这个观察很准确,但这正好是理解工厂方法的关键——它解决的不是"选哪个",而是"怎么创建 + 谁来决定"。

1. 选择逻辑确实没消失,但位置变了

选择从"业务代码"挪到"装配点"(Composition Root)。

没有工厂时:

// 业务代码里到处都有 if
public void a() { Payment p = type.equals("ali") ? new Alipay() : new Wechat(); }
public void b() { Payment p = type.equals("ali") ? new Alipay() : new Wechat(); }
// 改一次,N 处都要动

工厂方法后,选择集中到唯一入口:

// 整个系统只有这一处 if —— 这是"装配"而非"业务"
public PaymentFactory choose(String type) {
return type.equals("ali") ? new AlipayFactory() : new WechatFactory();
}

// 业务代码:不再出现任何类型判断
public void a(PaymentFactory f) { f.execute(order); }

2. 依赖的对象变了:具体产品 → 抽象工厂

简单工厂的 if-else 让工厂内部依赖了具体类(Alipay);工厂方法把选择上移后,业务代码依赖的是 PaymentFactory 抽象——耦合从"业务的每个角落"收敛为"唯一的装配处",加新类型只改装配点。

3. 工厂方法真正的价值不在"选择"

它解决的是另外两个问题:

  • 创建过程本身复杂:AlipayFactory 里可以组装配置、日志、连接池,这些细节不再泄漏到业务
  • 与模板方法配合:基类定死流程,子类只管"造什么对象"——选择权下放给了子类,调用方根本不用选

4. 什么时候工厂方法不适用

如果你的场景是运行时按 type 字符串动态选(比如支付类型来自前端),那工厂方法确实尴尬——选择点还是得 if。这种场景正解是注册表 + Map,由容器把选择和创建一起收走,调用方一行判断都没有:

// 调用方完全不接触任何 if/工厂子类
Payment p = PaymentRegistry.get(type); // DI 容器注入,类型映射藏在注册表里

结论:"选择逻辑留在调用方"是刻意为之:工厂方法的适用前提是选择在编译期/装配期就固定(每个调用方明确知道自己要什么),此时 if 只出现一次;如果是运行时动态选择,那应该用注册表/DI 容器,而不是工厂方法。判断标准:选择时机是"装配期"还是"运行期"。


四、什么时候不要用工厂

工厂不是银弹,用错反而添乱:

| 场景 | 建议 |

|------|------|

| 只有一个实现类,或构造简单 | 直接用 new,别为了模式而模式 |

| 无多态需求(不需要接口) | 不需要工厂 |

| 类型无限多(如用户自定义插件) | 工厂 + 注册表/反射/Map 更合适 |

| 需要按配置动态选择 | 工厂结合配置中心 / SPI 机制 |


五、进阶:工厂 + 注册表(生产中最常见的形态)

if-else 会随着类型增多越来越长,生产代码通常用注册表代替分支:

@Component
public class PaymentFactory {
private final Map<String, Payment> registry;

public PaymentFactory(List<Payment> all) { // Spring 自动注入所有实现
registry = all.stream()
.collect(Collectors.toMap(Payment::getType, p -> p));
}

public Payment create(String type) {
Payment p = registry.get(type);
if (p == null) throw new IllegalArgumentException("unknown: " + type);
return p;
}
}

加新支付方式 = 加一个 @Component 实现类,工厂代码一行都不用改。这是 Spring/Guice 里最标准的工厂写法。


六、一句话总结

工厂模式 = 把"创建对象"这个变化点封装起来,让调用方面向接口编程。简单工厂管分支,工厂方法管子类扩展,抽象工厂管产品族配套;生产落地时用注册表消除 if-else,配合 DI 容器实现零侵入扩展。

← 返回首页