Skip to content

工厂家族:简单工厂、工厂方法与抽象工厂

本文是设计模式系统学习系列的 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),不是简单工厂模式。区别:静态工厂方法直接返回一个实例,名字可以表达意图(ofvalueOfnewInstance),而简单工厂模式是一个类统一管理多个产品的创建逻辑

工厂方法:把选择权交给子类

工厂方法模式把"创建哪个产品"的决定权下沉到子类。父类定义创建框架(接口),子类决定具体实现。

改写支付渠道的例子

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 的 ConnectionStatementResultSet 就是一个产品族。不同数据库厂商(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; // 默认单例
    }
}

BeanFactoryFactoryBean 的区别:BeanFactory 是整个 IoC 容器,FactoryBean 是容器里一个特殊的 Bean。关键点在于:BeanFactory 是容器,FactoryBean 是工厂 Bean

常见误区与小结

  • 简单工厂不是"设计模式"——但实际工程里最常见,不必为名分纠结
  • 工厂方法不是省代码,是做扩展点——如果产品两年没变过,简单工厂就够了
  • 抽象工厂加新产品族很痛苦——接口一改,全部实现类都要改,设计时要想好产品族是否稳定
  • 静态工厂方法(valueOfof)不是工厂模式——只是创建对象的一种命名约定
  • Spring 的 @Bean 注解本质上就是工厂方法——一个方法返回一个对象,Spring 负责管理生命周期

小结:工厂模式的核心是把"创建"和"使用"分离,让调用方不依赖具体类。简单工厂适合产品少、变化不频繁的场景;工厂方法适合扩展频繁的场景;抽象工厂适合产品族成组出现的场景。下一篇看单例与建造者——对象创建的两个极端,一个保证只有一个实例,一个帮你优雅地构造复杂对象。

参考

参考:GoF《设计模式》第 3 章(创建型模式总论)、Spring 源码 org.springframework.beans.factory.FactoryBean、Effective Java 第 2 条(静态工厂方法)

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。
粤ICP备2026104257号-1