主题
工厂家族:简单工厂、工厂方法与抽象工厂
本文是设计模式系统学习系列的 L2 核心篇。前置:1. SOLID 与设计原则。 学完可以配合面试题食用:Spring IoC 容器初始化
为什么需要工厂模式
如果你的代码里到处是 new(),一旦构造参数变了、或者要换一个实现类,就得全文搜索替换。工厂模式把"创建对象"这件事包起来,调用方只问"我要一个 X",不管 X 怎么造的、要什么参数。这是依赖倒置(DIP)的落地:上层不依赖具体类,依赖接口,工厂负责把具体类实例化好塞给上层。
java
// 没有工厂:到处 new
OrderService service = new OrderService(new PaymentGateway(), new InventoryClient());
// 有工厂:调用方只关心接口
OrderService service = OrderServiceFactory.create();简单工厂:集中创建,但违反 OCP
简单工厂不是一个 GoF 正式模式,但实际代码里用得最多。它就是一个类专门负责创建对象,通过参数决定返回哪个子类。
支付渠道的简单工厂
java
public class PaymentFactory {
public static PaymentChannel create(String type) {
return switch (type) {
case "alipay" -> new AlipayChannel();
case "wechat" -> new WechatPayChannel();
case "unionpay" -> new UnionPayChannel();
default -> throw new IllegalArgumentException("Unknown channel: " + type);
};
}
}调用方这样写:
java
PaymentChannel channel = PaymentFactory.create("alipay");
channel.pay(amount);好处是创建逻辑集中了,改构造参数或换实现类只改一处工厂。但问题也明显:每新增一个渠道,就要改 switch 语句,违反了 OCP(对扩展开放、对修改关闭)。这就是简单工厂的代价——简单,但可扩展性差。
静态工厂方法不是简单工厂
Integer.valueOf(int)、Collections.emptyList() 这些也是"创建对象的方法",但它们是静态工厂方法(Static Factory Method),不是简单工厂模式。区别:静态工厂方法直接返回一个实例,名字可以表达意图(of、valueOf、newInstance),而简单工厂模式是一个类统一管理多个产品的创建逻辑。
工厂方法:把选择权交给子类
工厂方法模式把"创建哪个产品"的决定权下沉到子类。父类定义创建框架(接口),子类决定具体实现。
改写支付渠道的例子
java
// 抽象创建者
public abstract class PaymentService {
public void processPayment(double amount) {
PaymentChannel channel = createChannel();
channel.pay(amount);
}
protected abstract PaymentChannel createChannel(); // 工厂方法
}
// 具体创建者
public class AlipayService extends PaymentService {
@Override
protected PaymentChannel createChannel() {
return new AlipayChannel();
}
}
public class WechatPayService extends PaymentService {
@Override
protected PaymentChannel createChannel() {
return new WechatPayChannel();
}
}调用方:
java
PaymentService service = new AlipayService();
service.processPayment(100.0);新增渠道时,不需要改任何已有代码,只要加一个 XxxService 子类就行。这就是 OCP 的体现。
代价是类数量翻倍:每个产品对应一个工厂子类。如果产品只有两三种,工厂方法有点过度设计了。
抽象工厂:产品族的概念
抽象工厂解决的是产品族问题:你要创建的不是单个对象,而是一组配套的对象。
经典场景:跨平台 UI 控件
Windows 下的 Button 和 TextBox 是一族的,Mac 下的又是另一族。不能用 Windows 的 Button 配 Mac 的 TextBox。
java
// 抽象工厂:产品族
public interface UIFactory {
Button createButton();
TextBox createTextBox();
Checkbox createCheckbox();
}
// 具体工厂
public class WindowsUIFactory implements UIFactory {
public Button createButton() { return new WindowsButton(); }
public TextBox createTextBox() { return new WindowsTextBox(); }
public Checkbox createCheckbox() { return new WindowsCheckbox(); }
}
public class MacUIFactory implements UIFactory {
public Button createButton() { return new MacButton(); }
public TextBox createTextBox() { return new MacTextBox(); }
public Checkbox createCheckbox() { return new MacCheckbox(); }
}数据库驱动也是抽象工厂
JDBC 的 Connection、Statement、ResultSet 就是一个产品族。不同数据库厂商(MySQL / PostgreSQL / Oracle)各自实现这一族接口。DriverManager.getConnection() 返回的 Connection 是 MySQL 还是 PG 的,取决于驱动类加载了哪个 Driver。
抽象工厂的代价:产品族里新增一个产品很难。要在 UIFactory 接口上加一个 createSlider(),所有实现类都得改。这就是"抽象工厂的可扩展性不对称"——横向(新产品)难加,纵向(新平台)好加。
三者选型
| 阶段 | 用什么 | 什么时候升级 |
|---|---|---|
| 只创建一种产品,扩展少 | 简单工厂 | 产品种类超过 5 个,switch 老改就升级 |
| 产品种类多,扩展频繁 | 工厂方法 | 子类膨胀到 10+ 时可考虑降级为配置驱动 |
| 配套产品成族出现 | 抽象工厂 | 产品族变化不大(如只有两家平台)时好用 |
支付渠道的场景,很多项目就是用简单工厂硬扛的——因为产品种类也就 3-5 种,没那么频繁变。不必为了"设计模式"强上工厂方法。
动手实操
三种工厂实现支付渠道对比
java
// ========== 同一套产品接口 ==========
interface PaymentChannel {
void pay(double amount);
boolean refund(String tradeNo);
}
class AlipayChannel implements PaymentChannel {
public void pay(double amount) { System.out.println("支付宝支付 " + amount); }
public boolean refund(String tradeNo) { System.out.println("支付宝退款 " + tradeNo); return true; }
}
class WechatPayChannel implements PaymentChannel {
public void pay(double amount) { System.out.println("微信支付 " + amount); }
public boolean refund(String tradeNo) { System.out.println("微信退款 " + tradeNo); return true; }
}
// ========== 方案一:简单工厂 ==========
class PaymentSimpleFactory {
static PaymentChannel create(String type) {
return switch (type) {
case "alipay" -> new AlipayChannel();
case "wechat" -> new WechatPayChannel();
default -> throw new IllegalArgumentException();
};
}
}
// ========== 方案二:工厂方法 ==========
abstract class PaymentService {
abstract PaymentChannel createChannel();
void pay(double amount) { createChannel().pay(amount); }
}
class AlipayService extends PaymentService {
PaymentChannel createChannel() { return new AlipayChannel(); }
}
class WechatPayService extends PaymentService {
PaymentChannel createChannel() { return new WechatPayChannel(); }
}
// ========== 方案三:抽象工厂(产品族:支付+退款+对账) ==========
interface PaymentFactory {
PaymentChannel createChannel();
ReconciliationService createReconciliation();
}
class AlipayPaymentFactory implements PaymentFactory {
public PaymentChannel createChannel() { return new AlipayChannel(); }
public ReconciliationService createReconciliation() { return new AlipayReconciliation(); }
}
class WechatPaymentFactory implements PaymentFactory {
public PaymentChannel createChannel() { return new WechatPayChannel(); }
public ReconciliationService createReconciliation() { return new WechatReconciliation(); }
}Spring 的 FactoryBean
FactoryBean 是 Spring 自己的工厂模式扩展。一个 Bean 如果是 FactoryBean,Spring 容器在获取时会调用它的 getObject() 方法返回对象,而不是返回 FactoryBean 本身。
java
@Component
public class MyServiceFactoryBean implements FactoryBean<MyService> {
@Override
public MyService getObject() {
// 这里可以做复杂的创建逻辑
MyService service = new MyService();
service.setTimeout(5000);
service.setRetry(3);
// 返回的不是 FactoryBean 本身,而是 MyService 实例
return service;
}
@Override
public Class<?> getObjectType() {
return MyService.class;
}
@Override
public boolean isSingleton() {
return true; // 默认单例
}
}BeanFactory 和 FactoryBean 的区别:BeanFactory 是整个 IoC 容器,FactoryBean 是容器里一个特殊的 Bean。关键点在于:BeanFactory 是容器,FactoryBean 是工厂 Bean。
常见误区与小结
- 简单工厂不是"设计模式"——但实际工程里最常见,不必为名分纠结
- 工厂方法不是省代码,是做扩展点——如果产品两年没变过,简单工厂就够了
- 抽象工厂加新产品族很痛苦——接口一改,全部实现类都要改,设计时要想好产品族是否稳定
- 静态工厂方法(
valueOf、of)不是工厂模式——只是创建对象的一种命名约定 - Spring 的
@Bean注解本质上就是工厂方法——一个方法返回一个对象,Spring 负责管理生命周期
小结:工厂模式的核心是把"创建"和"使用"分离,让调用方不依赖具体类。简单工厂适合产品少、变化不频繁的场景;工厂方法适合扩展频繁的场景;抽象工厂适合产品族成组出现的场景。下一篇看单例与建造者——对象创建的两个极端,一个保证只有一个实例,一个帮你优雅地构造复杂对象。
参考
参考:GoF《设计模式》第 3 章(创建型模式总论)、Spring 源码
org.springframework.beans.factory.FactoryBean、Effective Java 第 2 条(静态工厂方法)