Java 中的 Factory Method 和 Abstract Factory

1. 概述

Factory Method 和 Abstract Factory 都属于 creational pattern,都能帮你创建对象而不需要直接 new 具体的类。但这两个模式解决的问题不同,初学者很容易混淆。

在这篇文章中,你将通过 PaymentMethod 例子(Factory Method)和根据主题创建 UI 的系统(Abstract Factory)看到区别。读完之后,你会知道什么时候只需要一个简单的工厂,什么时候需要一个“生成工厂的工厂”。

2. 什么是 Factory Method?

Factory Method 定义了一个方法来创建对象,但由子类决定要创建哪个具体的类。简单来说:你有一个 interface 方法 create(),每个子类重写它以返回不同的实现。

2.1. 需要解决的问题

假设你有一个支付系统,支持多种类型:CreditCardPayment、MomoPayment、BankTransferPayment。如果客户端代码直接 new:

public void processOrder(Order order, String type) {
    PaymentMethod payment;
    if (type.equals("CREDIT_CARD")) {
        payment = new CreditCardPayment();
    } else if (type.equals("MOMO")) {
        payment = new MomoPayment();
    } else {
        payment = new BankTransferPayment();
    }
    payment.pay(order.getAmount());
}

每次添加新的支付类型时,你必须在使用它的每个地方修改这个 if-else。这正是 Factory Method 要解决的代码坏味道。

2.2. 应用 Factory Method

public interface PaymentMethod {
    void pay(BigDecimal amount);
}

public class CreditCardPayment implements PaymentMethod {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付 " + amount + " 使用信用卡");
    }
}

public class MomoPayment implements PaymentMethod {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付 " + amount + " 通过 Momo");
    }
}

说明:PaymentMethod 是公共接口,每个实现只负责自己的支付逻辑。这是工厂将创建的“产品”(product)。

接下来是包含 Factory Method 的 abstract class:

public abstract class PaymentProcessor {

    // Factory Method - 子类将决定创建哪个实例
    protected abstract PaymentMethod createPaymentMethod();

    public void processOrder(Order order) {
        PaymentMethod payment = createPaymentMethod();
        payment.pay(order.getAmount());
    }
}

public class CreditCardProcessor extends PaymentProcessor {
    @Override
    protected PaymentMethod createPaymentMethod() {
        return new CreditCardPayment();
    }
}

public class MomoProcessor extends PaymentProcessor {
    @Override
    protected PaymentMethod createPaymentMethod() {
        return new MomoPayment();
    }
}
  • createPaymentMethod() 是 Factory Method:它是一个抽象方法,每个子类重写它以返回正确的 PaymentMethod 类型。
  • processOrder() 是共用“模板”,不需要知道正在创建哪种支付类型的细节。

客户端这样使用:

PaymentProcessor processor = new MomoProcessor();
processor.processOrder(new Order(new BigDecimal("150000")));
支付 150000 通过 Momo

2.3. 测试 Factory Method

class PaymentProcessorTest {

    @Test
    void shouldCreateCorrectPaymentMethod() {
        PaymentProcessor processor = new CreditCardProcessor();
        PaymentMethod method = processor.createPaymentMethod();

        assertThat(method).isInstanceOf(CreditCardPayment.class);
    }
}

**注意:**为了测试能调用 createPaymentMethod()(protected),将测试类放在与 PaymentProcessor 相同的包中,或者如果需要从外部测试,将方法改为 public。

3. 什么是 Abstract Factory?

Abstract Factory 提供了一个接口来创建一族(family)相关的对象,而不需要指定每个对象的具体类。与 Factory Method 只创建一种产品不同,Abstract Factory 创建多个必须“一起配套”的产品。

3.1. 需要解决的问题

假设你为应用程序制作 UI 主题:“Light”主题和“Dark”主题。每个主题需要有自己的 Button 和 Checkbox,这两个必须在样式上保持一致 — 不能混合 LightButton 和 DarkCheckbox。

public interface Button {
    void render();
}

public interface Checkbox {
    void render();
}

public class LightButton implements Button {
    @Override
    public void render() {
        System.out.println("渲染白色背景、黑色文字的按钮");
    }
}

public class DarkButton implements Button {
    @Override
    public void render() {
        System.out.println("渲染黑色背景、白色文字的按钮");
    }
}

public class LightCheckbox implements Checkbox {
    @Override
    public void render() {
        System.out.println("渲染浅灰色边框的复选框");
    }
}

public class DarkCheckbox implements Checkbox {
    @Override
    public void render() {
        System.out.println("渲染白色边框的复选框");
    }
}

3.2. 定义 Abstract Factory

public interface UiComponentFactory {
    Button createButton();
    Checkbox createCheckbox();
}

public class LightThemeFactory implements UiComponentFactory {
    @Override
    public Button createButton() {
        return new LightButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new LightCheckbox();
    }
}

public class DarkThemeFactory implements UiComponentFactory {
    @Override
    public Button createButton() {
        return new DarkButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new DarkCheckbox();
    }
}

UiComponentFactory 是定义整个产品族(Button + Checkbox)的接口。每个实现(LightThemeFactory、DarkThemeFactory)确保创建的产品在主题上始终一致。

3.3. 在客户端中使用 Abstract Factory

public class ThemedApplication {

    private final UiComponentFactory factory;

    public ThemedApplication(UiComponentFactory factory) {
        this.factory = factory;
    }

    public void renderUi() {
        Button button = factory.createButton();
        Checkbox checkbox = factory.createCheckbox();
        button.render();
        checkbox.render();
    }
}
ThemedApplication app = new ThemedApplication(new DarkThemeFactory());
app.renderUi();
渲染黑色背景、白色文字的按钮
渲染白色边框的复选框

ThemedApplication 不知道也不需要知道正在创建哪个具体的类。它只依赖于 UiComponentFactory 接口,所以要更改主题,只需更改注入的工厂即可。

3.4. 测试 Abstract Factory

class ThemedApplicationTest {

    @Test
    void shouldCreateConsistentComponentsForDarkTheme() {
        UiComponentFactory factory = new DarkThemeFactory();

        Button button = factory.createButton();
        Checkbox checkbox = factory.createCheckbox();

        assertThat(button).isInstanceOf(DarkButton.class);
        assertThat(checkbox).isInstanceOf(DarkCheckbox.class);
    }
}

这个测试确认了 Abstract Factory 最重要的属性:从同一个工厂创建的产品总是属于同一个“族”,不会混合 Light 和 Dark。

4. 比较 Factory Method 和 Abstract Factory

标准 Factory Method Abstract Factory
产品数量 1 种产品(PaymentMethod) 多种相关产品(Button、Checkbox)
机制 在子类中重写一个方法 将多个工厂方法组合到一个接口中
关系 继承(Inheritance) 通常使用组合(inject factory)
目的 使客户端不必知道一个对象的具体类 确保多个相关对象之间的一致性

一个简单的记忆方法:Abstract Factory 通常通过一组 Factory Method 来实现。在上面的例子中,UiComponentFactory 中的 createButton() 和 createCheckbox() 正是两个子 Factory Method。

4.1. 何时使用 Factory Method

  • 你只需要创建一种对象,但创建逻辑很复杂或根据上下文变化。
  • 你在使用类层次结构,想让子类自己决定将使用哪种子对象类型(template method pattern 通常与此模式一起使用)。

4.2. 何时使用 Abstract Factory

  • 你需要创建多个相关对象,并且必须确保它们相互兼容(相同主题、相同数据库供应商、相同平台…)。
  • 系统可以扩展添加新的“产品族”(例如添加 HighContrastThemeFactory),而不想修改客户端代码。

**注意:**当你只有一种产品时不要使用 Abstract Factory — 它会创建一个冗余的接口层。简单的 Factory Method 就足够了。

5. 与 Spring Boot 集成

在 Spring 中,你很少按照 GoF 的确切结构手写 Factory Method/Abstract Factory,因为 Spring 容器已经充当了工厂的角色。然而,理解这个思想能帮你更好地设计 bean。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
@Configuration
public class PaymentFactoryConfig {

    @Bean
    public Map<String, PaymentMethod> paymentMethods() {
        return Map.of(
            "CREDIT_CARD", new CreditCardPayment(),
            "MOMO", new MomoPayment()
        );
    }
}
@Service
public class PaymentService {

    private final Map<String, PaymentMethod> paymentMethods;

    public PaymentService(Map<String, PaymentMethod> paymentMethods) {
        this.paymentMethods = paymentMethods;
    }

    public void pay(String type, BigDecimal amount) {
        PaymentMethod method = paymentMethods.get(type);
        if (method == null) {
            throw new IllegalArgumentException("不支持的支付类型:" + type);
        }
        method.pay(amount);
    }
}

这里,Spring 容器充当了真正的“工厂”:它预先创建 PaymentMethod bean,客户端只需按键查询。这是 Spring 环境中 Factory Method 的实用变体 — 你不再需要为每种支付类型编写单独的 XxxProcessor 类。

6. 常见错误

  • 将 Factory Method 与静态工厂方法混淆(如 Optional.of()、List.of())。静态工厂方法只是比构造函数更简洁的对象创建方式,与 GoF 模式的多态性无关。
  • 当只有一个产品时滥用 Abstract Factory:如果你只有 Button 而没有随之而来的 Checkbox,Abstract Factory 就过度设计了。
  • 让工厂知道太多的业务逻辑:工厂应该只负责创建对象,不应该在其中嵌入订单处理、验证等逻辑。
  • 忘记确保 Abstract Factory 中的一致性:如果你允许客户端从工厂 A 自己选择 Button,从工厂 B 选择 Checkbox,你就破坏了该模式的主要目的。

7. 结论

Factory Method 适合当你需要将一个对象的创建委托给子类时,根据开闭原则帮助代码易于扩展。Abstract Factory 更进一步:它确保一个相关对象的族总是被一致地创建,避免混合不兼容的组件。

在 Spring Boot 的实践中,你通常不会按照 GoF 结构手写,而是利用 DI 容器作为工厂。但清楚理解这两个模式的本质能帮你更好地理解库代码(例如 JDBC 中的 DocumentBuilderFactory、ConnectionFactory)并设计更好的可扩展模块。

design-patternjavacreational-patternspring-bootclean-code