工厂模式平时在项目里几乎天天碰(Spring 的 @Bean、BeanFactory 本质就是工厂),但"简单工厂、工厂方法、抽象工厂"这三个词到底什么关系、什么时候用哪个,一直没有清晰地串起来。这篇按演进顺序理一遍,每级解决什么问题、又带来什么新问题。
一、解决什么问题
工厂模式解决的是 new 的硬编码耦合。
// 写死具体类:想换成微信支付,就得改这行代码
Pay pay = new Alipay();一旦代码里到处 new Alipay(),切换实现、增加渠道、统一初始化逻辑,全都要改源码。工厂模式把"创建对象的逻辑"从"使用对象的地方"抽出来,集中到一个地方管理。
核心思想一句话:不直接 new,而是问工厂要。
二、演进:简单工厂 → 工厂方法 → 抽象工厂
v1 简单工厂(Simple Factory):一个工厂包办所有
public class PayFactory {
public static Pay create(String type) {
switch (type) {
case "alipay": return new Alipay();
case "wechat": return new WechatPay();
default: throw new IllegalArgumentException("不支持的支付方式: " + type);
}
}
}
// 使用方
Pay pay = PayFactory.create("alipay");
pay.pay(100);一个工厂类 + 一个静态方法,根据参数 switch 返回不同产品。优点:把创建逻辑集中了,使用方不再 new。缺点:每加一种支付方式,就要改 switch——违反开闭原则(对修改不关闭)。产品一多,switch 越来越长。
v2 工厂方法(Factory Method):每个产品一个工厂
解决简单工厂"加产品要改工厂"的问题——把工厂也抽象成接口,每个产品配一个工厂类:
public interface PayFactory {
Pay create();
}
public class AlipayFactory implements PayFactory {
public Pay create() { return new Alipay(); }
}
public class WechatFactory implements PayFactory {
public Pay create() { return new WechatPay(); }
}
// 使用方
PayFactory factory = new AlipayFactory();
Pay pay = factory.create();现在加一种支付方式 = 新增一个产品类 + 新增一个工厂类,完全不改已有代码,符合开闭原则。代价:类爆炸——每加一个产品就多一个工厂类,产品多的时候类数量翻倍。所以工厂方法适合"产品种类固定、但要灵活扩展"的场景。
v3 抽象工厂(Abstract Factory):一族相关产品
工厂方法一次只造一个产品。但当产品是成族出现的(比如一个支付渠道同时提供"支付"和"退款"两个能力),抽象工厂就来了:
public interface PayFactory {
Pay createPay(); // 一族产品:支付 + 退款
Refund createRefund();
}
public class AlipayFactory implements PayFactory {
public Pay createPay() { return new AlipayPay(); }
public Refund createRefund() { return new AlipayRefund(); }
}
// 使用方:换一个工厂,整套产品族一起换
PayFactory factory = new AlipayFactory();
Pay pay = factory.createPay();
Refund refund = factory.createRefund();抽象工厂保证"同一族产品之间的匹配"——不会出现"支付宝的支付 + 微信的退款"这种错配。换渠道时,换一个工厂,整族产品一起切。
三、三种工厂对比
| 维度 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 工厂数量 | 1 个 | 每个产品 1 个 | 每个产品族 1 个 |
| 创建对象 | 一种(按参数分支) | 一种 | 一族(多个相关) |
| 加产品 | 改 switch(违反开闭) | 加类(不改旧代码) | 加族(改接口) |
| 复杂度 | 低 | 中 | 高 |
| 典型场景 | 产品少、简单 | 产品多、需扩展 | 产品成族、需匹配 |
四、Spring 里的工厂
Spring 本身就是个巨型工厂,工厂模式贯穿其中:
BeanFactory/ApplicationContext:就是抽象工厂——getBean("name")从容器里拿对象,而不是new@Bean注解方法:就是工厂方法——方法返回的对象交给容器管理
@Configuration
public class PayConfig {
@Bean
public Pay alipay() { // 工厂方法:@Bean 方法名就是产品名
return new Alipay();
}
}这和单例模式也有关联——Spring 的 Bean 默认单例,工厂返回的往往是同一个实例(见《单例模式》)。
小结
- 工厂模式解决
new的硬编码耦合,把创建逻辑从使用处抽离 - 演进线:简单工厂(一个工厂
switch,违反开闭)→ 工厂方法(每产品一工厂,符合开闭但类爆炸)→ 抽象工厂(一族产品一工厂,保证族内匹配) - 简单工厂适合产品少,工厂方法适合产品多需扩展,抽象工厂适合产品成族
- Spring 的
BeanFactory、@Bean就是工厂模式的落地
下一步可以看同样高频的《策略模式》——它和工厂模式经常配合(工厂负责"创建哪个策略",策略负责"执行哪种算法"),两者放一起对比更容易分清(文章整理中)。
