跳转至

工厂方法模式

从披萨连锁店说起

你的披萨店大受欢迎,准备在纽约和芝加哥开连锁店。但两个城市的披萨口味不同——纽约用薄脆饼底,芝加哥用厚脆饼底;切法也不同。如果把创建逻辑写在 PizzaStore.orderPizza() 里,就变成:

1
2
3
4
5
if (style.equals("ny")) {
    pizza = new NYStyleCheesePizza();
} else if (style.equals("chicago")) {
    pizza = new ChicagoStyleCheesePizza();
}

这意味着每开一个新城市,都要修改 PizzaStore——把变化的部分("创建哪种披萨")与不变的部分("准备-烘烤-切-打包的流程")耦合在了一起。

解决方案:createPizza() 声明为抽象方法,让子类来决定实例化哪种 Pizza。

🔍 定义

工厂方法模式(Factory Method)定义一个创建对象的接口,由子类决定实例化哪个具体类。工厂方法让创建逻辑推迟到子类,从而实现创建与使用的解耦。

设计原则:依赖倒置(DIP)—— 高层组件不应该依赖低层组件,两者都应该依赖抽象。 PizzaStore(高层)不依赖 NYStyleCheesePizza(低层),而是依赖 Pizza(抽象)。

⚠️ 不使用该模式存在的问题

FactoryMethodBadExample.java
package com.example.creational.factory_method;

/**
 * 工厂方法模式 - 反例
 * 问题1:直接 new,与具体类强耦合
 * 问题2:if-else 扩展需要修改原有代码,违反开闭原则
 */
public class FactoryMethodBadExample {
    public static void main(String[] args) {
        // 方式1:直接 new,与具体类强耦合 ❌
        AlipayNotificationBad alipay = new AlipayNotificationBad();
        alipay.send("支付宝订单:123");

        // 方式2:字符串 type 控制分支,新增类型必须改这里 ❌
        OrderServiceBad service = new OrderServiceBad();
        service.completeOrder("alipay",  1L);
        service.completeOrder("wechat",  2L);
        service.completeOrder("unknown", 3L);
    }
}

// 通知方式接口
interface NotificationBad {
    void send(String msg);
}

// 支付宝通知(具体类)
class AlipayNotificationBad implements NotificationBad {
    @Override
    public void send(String msg) { System.out.println("[支付宝通知] " + msg); }
}

// 微信通知(具体类)
class WechatNotificationBad implements NotificationBad {
    @Override
    public void send(String msg) { System.out.println("[微信通知] " + msg); }
}

// ❌ 直接在业务代码里写 if-else 判断类型
class OrderServiceBad {
    public void completeOrder(String paymentType, Long orderId) {
        // 处理业务逻辑...
        NotificationBad notification;
        if ("alipay".equals(paymentType)) {
            notification = new AlipayNotificationBad(); // ❌ 强耦合
        } else if ("wechat".equals(paymentType)) {
            notification = new WechatNotificationBad(); // ❌ 强耦合
        } else {
            throw new IllegalArgumentException("不支持的支付类型");
        }
        notification.send("订单 " + orderId + " 已完成");
    }
}

🏗️ 设计模式结构(披萨连锁店)

%%{init: {'themeVariables': {'noteBkgColor': 'transparent', 'noteBorderColor': '#768390'}}}%%
classDiagram
    classDef default fill:transparent,stroke:#768390
    class FMPizzaStore {
        <<abstract>>
        +createPizza(type) FMPizza*
        +orderPizza(type) FMPizza
    }
    class NYPizzaStore {
        +createPizza(type) FMPizza
    }
    class ChicagoPizzaStore {
        +createPizza(type) FMPizza
    }
    class FMPizza {
        <<abstract>>
        #name: String
        +prepare() void
        +bake() void
        +cut() void
        +box() void
    }
    class NYStyleCheesePizza {
    }
    class ChicagoStyleCheesePizza {
    }
    FMPizzaStore <|-- NYPizzaStore
    FMPizzaStore <|-- ChicagoPizzaStore
    FMPizza <|-- NYStyleCheesePizza
    FMPizza <|-- ChicagoStyleCheesePizza
    NYPizzaStore ..> NYStyleCheesePizza
    ChicagoPizzaStore ..> ChicagoStyleCheesePizza
    note for FMPizzaStore "抽象创建者(Creator)"
    note for NYPizzaStore "具体创建者(ConcreteCreator)"
    note for FMPizza "抽象产品(Product)"
    note for NYStyleCheesePizza "具体产品(ConcreteProduct)"

核心角色:

角色 说明
FMPizzaStore(抽象创建者) 声明工厂方法,包含 orderPizza() 业务骨架
NYPizzaStore(具体创建者) 实现工厂方法,决定创建纽约风格的 Pizza
FMPizza(抽象产品) 所有 Pizza 的公共接口
NYStyleCheesePizza(具体产品) 纽约风格的具体 Pizza

💻 设计模式举例说明

FactoryMethodExample.java
package com.example.creational.factory_method;

/**
 * 工厂方法模式 - 正例
 * 工厂方法由子类决定创建哪种产品,新增支付方式只需添加新子类,不修改已有代码
 */
public class FactoryMethodExample {
    public static void main(String[] args) {
        // ✅ 程序只依赖抽象 PaymentFactory,新增支付方式不改原有代码
        PaymentFactory alipayFactory = new AlipayFactory();
        PaymentResult r1 = alipayFactory.processPayment(99.9);
        System.out.println("支付宝:" + r1.status() + " - " + r1.message());

        PaymentFactory wechatFactory = new WechatPayFactory();
        PaymentResult r2 = wechatFactory.processPayment(99.9);
        System.out.println("微信支付:" + r2.status() + " - " + r2.message());
    }
}

// 支付结果状态
enum PaymentStatus { SUCCESS, FAILED }

// 支付结果值对象
record PaymentResult(PaymentStatus status, String message) {}

// 产品接口
interface PaymentProcessor {
    PaymentResult pay(double amount);
}

// 具体产品:支付宝
class AlipayProcessor implements PaymentProcessor {
    @Override
    public PaymentResult pay(double amount) {
        System.out.println("[支付宝] 扣款 " + amount + " 元");
        return new PaymentResult(PaymentStatus.SUCCESS, "支付宝支付成功");
    }
}

// 具体产品:微信支付
class WechatPayProcessor implements PaymentProcessor {
    @Override
    public PaymentResult pay(double amount) {
        System.out.println("[微信支付] 扣款 " + amount + " 元");
        return new PaymentResult(PaymentStatus.SUCCESS, "微信支付成功");
    }
}

// 抽象工厂:定义工厂方法
abstract class PaymentFactory {
    // 工厂方法:由子类决定创建哪种 PaymentProcessor
    protected abstract PaymentProcessor createProcessor();

    // 模板方法:完整的支付流程
    public PaymentResult processPayment(double amount) {
        PaymentProcessor processor = createProcessor(); // 调用工厂方法
        return processor.pay(amount);
    }
}

// 具体工厂:支付宝
class AlipayFactory extends PaymentFactory {
    @Override
    protected PaymentProcessor createProcessor() {
        return new AlipayProcessor(); // ✅ 子类决定创建哪种产品
    }
}

// 具体工厂:微信支付
class WechatPayFactory extends PaymentFactory {
    @Override
    protected PaymentProcessor createProcessor() {
        return new WechatPayProcessor(); // ✅ 子类决定创建哪种产品
    }
}

工厂方法 = 模板方法 + 产品创建

PizzaStore.orderPizza() 是模板方法,它定义了"准备→烘烤→切→打包"的算法框架;createPizza() 是工厂方法,嵌入在模板方法中。子类通过实现 createPizza() 同时实现了"创建正确的产品"和"参与到模板流程中"。

⚖️ 优缺点

优点:

  • 符合**开闭原则**:新增城市(新增子类)不修改已有代码
  • 符合**依赖倒置**:PizzaStore 只依赖抽象 Pizza,不依赖任何具体 Pizza 类
  • 创建与使用解耦,可以灵活配置

缺点:

  • 类数量增多:每新增一种产品类型,就需要新增工厂子类 + 产品子类
  • 对于简单场景,工厂方法反而过度设计

🔗 与其它模式的关系

相关模式 关系说明
抽象工厂 抽象工厂是工厂方法的扩展——包含多个工厂方法,创建一整族产品
模板方法 工厂方法通常嵌入在模板方法中使用
原型 当产品创建代价高时,可用克隆替代工厂方法

🗂️ 应用场景

  • 多渠道支付、多格式文档生成、多数据库连接
  • JDK:Collection.iterator() 是工厂方法的经典体现(每种集合返回自己的迭代器)
  • Spring BeanFactory.getBean() 根据配置决定创建哪种 Bean 实例

🏭 工业视角

工厂方法用多态消除了 if-else,但并未消除选择逻辑

工厂方法的经典结构是:为每种产品创建一个对应的工厂子类,通过多态来替代条件分支。新增一种 Parser,只需新增一个 Factory 实现,原有代码不动——符合开闭原则。

工厂方法:多态替代 if-else
// 每种 Parser 对应一个工厂,新增类型只需新增工厂类
public interface IRuleConfigParserFactory {
    IRuleConfigParser createParser();
}

public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new JsonRuleConfigParser();
    }
}

但问题随之而来:调用方如何选择使用哪个工厂? 往往还需要一个 if-else 来决定实例化哪个 Factory。此时通常用一个简单工厂(或 Map)来管理 Factory 的选择,形成"工厂的工厂"结构。

工厂方法适合创建逻辑本身复杂的场景

工厂方法真正的价值不在于消除 if-else,而在于:当不同类型对象的创建过程差异很大,把复杂的创建逻辑封装在各自的 Factory 子类中,职责清晰,互不干扰。

如果只是 new XxxParser(),三行以内能写完,用工厂方法反而是过度设计——多出 N 个工厂类,代码量翻倍,可读性下降。

工厂方法的代价

引入工厂方法会显著增加类的数量:每种产品需要一个 Factory 子类。 在类型较多且创建逻辑简单的场景下,简单工厂(一个类 + if-else 或 Map)通常是更务实的选择。