Skip to content

工厂方法模式 (Factory Method Pattern)

一、定义

一句话概括:定义一个用于创建对象的接口,让子类决定实例化哪一个类,使类的实例化延迟到子类。

官方定义(GoF):Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

工厂方法模式的核心思想是:将对象的创建和使用分离。客户端不需要知道具体产品的类名,只需要知道对应的工厂即可。


二、解决的问题

2.1 什么场景下需要工厂方法模式?

  • 对象创建逻辑复杂:创建对象时需要进行大量初始化、配置或条件判断。
  • 需要解耦客户端和具体产品类:客户端不依赖具体产品,只依赖抽象接口。
  • 需要扩展产品族:新增产品时,只需新增一个工厂类,无需修改已有代码(符合开闭原则)。
  • 同一套代码需要创建不同产品:如日志框架支持控制台输出、文件输出、远程输出等。

2.2 不用工厂方法会有什么问题?

假设一个支付系统,客户端直接创建具体支付对象:

java
// 问题代码:客户端直接依赖具体类
if (payType.equals("ALIPAY")) {
    payment = new AliPay();       // 强耦合
} else if (payType.equals("WECHAT")) {
    payment = new WeChatPay();    // 强耦合
} else if (payType.equals("UNION")) {
    payment = new UnionPay();     // 强耦合
}
payment.pay(amount);

问题分析

  • 新增支付方式时,所有调用方代码都要修改(违反开闭原则)
  • 客户端与具体支付类强耦合,难以单元测试
  • 创建逻辑散落在各处,难以统一管理

三、结构

3.1 文字描述

工厂方法模式包含四个角色:

  • Product(抽象产品):定义产品的接口
  • ConcreteProduct(具体产品):实现抽象产品接口
  • Creator(抽象工厂):声明工厂方法,返回 Product 类型
  • ConcreteCreator(具体工厂):实现工厂方法,返回具体的 ConcreteProduct

3.2 ASCII 类图

┌──────────────────────┐          ┌──────────────────────┐
│    <<interface>>      │          │    <<abstract>>       │
│       Product         │<─────────│       Creator         │
├──────────────────────┤          ├──────────────────────┤
│ + operation(): void   │          │ + factoryMethod()     │
└──────────────────────┘          │   : Product            │
          △                       │ + someOperation()      │
          │                       └──────────────────────┘
          │                                  △
    ┌─────┴─────┐                            │
    │           │                  ┌──────────┴──────────┐
┌──────────┐ ┌──────────┐        │                      │
│ConcreteProductA│ConcreteProductB│  ┌──────────────────┐ ┌──────────────────┐
├──────────┤ ├──────────┤  │ConcreteCreatorA   │ │ConcreteCreatorB   │
│+operation│ │+operation│  ├──────────────────┤ ├──────────────────┤
└──────────┘ └──────────┘  │+factoryMethod()  │ │+factoryMethod()  │
                            │  → new ProductA()│ │  → new ProductB()│
                            └──────────────────┘ └──────────────────┘

四、代码实现

4.1 基础实现

4.1.1 简单工厂(Simple Factory)—— 工厂方法的"前身"

java
/**
 * 简单工厂 —— 不是 GoF 23 种设计模式之一,但很常用
 * 优点:客户端只需传参,无需关心创建细节
 * 缺点:新增产品时需要修改工厂类(违反开闭原则)
 */
public class SimpleFactory {

    public static Product createProduct(String type) {
        switch (type.toUpperCase()) {
            case "A": return new ProductA();
            case "B": return new ProductB();
            default: throw new IllegalArgumentException("未知产品类型: " + type);
        }
    }
}

// ========== 产品接口 ==========
interface Product {
    void operate();
}

// ========== 具体产品 ==========
class ProductA implements Product {
    @Override
    public void operate() {
        System.out.println("ProductA 执行业务逻辑");
    }
}

class ProductB implements Product {
    @Override
    public void operate() {
        System.out.println("ProductB 执行业务逻辑");
    }
}

4.1.2 工厂方法模式(标准实现)

java
/**
 * 日志记录器工厂方法模式 —— 完整实现
 */
public class FactoryMethodDemo {

    // ========== 1. 抽象产品 ==========
    public interface Logger {
        void log(String message);
    }

    // ========== 2. 具体产品 ==========
    public static class ConsoleLogger implements Logger {
        @Override
        public void log(String message) {
            System.out.println("[Console] " + message);
        }
    }

    public static class FileLogger implements Logger {
        private final String filePath;

        public FileLogger(String filePath) {
            this.filePath = filePath;
        }

        @Override
        public void log(String message) {
            System.out.println("[File: " + filePath + "] " + message);
            // 实际项目中:写入文件
        }
    }

    public static class DatabaseLogger implements Logger {
        @Override
        public void log(String message) {
            System.out.println("[Database] " + message);
            // 实际项目中:写入数据库
        }
    }

    // ========== 3. 抽象工厂 ==========
    public interface LoggerFactory {
        Logger createLogger();
    }

    // ========== 4. 具体工厂 ==========
    public static class ConsoleLoggerFactory implements LoggerFactory {
        @Override
        public Logger createLogger() {
            return new ConsoleLogger();
        }
    }

    public static class FileLoggerFactory implements LoggerFactory {
        @Override
        public Logger createLogger() {
            return new FileLogger("/var/log/app.log");
        }
    }

    public static class DatabaseLoggerFactory implements LoggerFactory {
        @Override
        public Logger createLogger() {
            // 可以在此处做复杂的初始化:建立数据库连接、验证权限等
            return new DatabaseLogger();
        }
    }

    // ========== 5. 客户端代码 ==========
    public static void main(String[] args) {
        // 客户端只需要知道工厂接口,不需要知道具体产品
        LoggerFactory factory = new ConsoleLoggerFactory();
        Logger logger = factory.createLogger();
        logger.log("用户登录成功");

        // 切换日志方式只需换一个工厂
        factory = new FileLoggerFactory();
        logger = factory.createLogger();
        logger.log("文件写入测试");

        // 新增日志方式:只需新增 Logger 实现 + Factory 实现,无需修改已有代码
        factory = new DatabaseLoggerFactory();
        logger = factory.createLogger();
        logger.log("数据库写入测试");
    }
}

4.2 进阶实现

4.2.1 带反射的工厂方法

java
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 带反射的工厂方法 —— 通过配置或注解动态创建产品
 * 优点:新增产品时只需添加产品类,无需修改工厂
 */
public class ReflectiveLoggerFactory {

    // 缓存 Logger 实例(单例)
    private static final Map<String, Logger> cache = new ConcurrentHashMap<>();

    /**
     * 通过反射创建 Logger
     * 实际项目中,className 可来自配置文件或注解
     */
    @SuppressWarnings("unchecked")
    public static Logger createLogger(String className) {
        // 先从缓存获取
        Logger cached = cache.get(className);
        if (cached != null) {
            return cached;
        }

        try {
            Class<?> clazz = Class.forName(className);
            Logger instance = (Logger) clazz.getDeclaredConstructor().newInstance();
            cache.put(className, instance);
            return instance;
        } catch (Exception e) {
            throw new RuntimeException("无法创建 Logger: " + className, e);
        }
    }

    // ========== 测试 ==========
    public static void main(String[] args) {
        Logger logger = ReflectiveLoggerFactory
                .createLogger("FactoryMethodDemo$ConsoleLogger");
        logger.log("反射工厂创建成功");
    }
}

4.2.2 泛型工厂方法

java
/**
 * 泛型工厂方法 —— 类型安全的产品创建
 */
public class GenericFactory {

    // ========== 泛型工厂接口 ==========
    public interface Factory<T> {
        T create();
    }

    // ========== 具体工厂(使用 Lambda 简化) ==========
    public static void main(String[] args) {
        // Java 8+ 可以使用 Lambda 简化工厂创建
        Factory<Logger> consoleFactory = ConsoleLogger::new;
        Factory<Logger> fileFactory = () -> new FileLogger("/tmp/app.log");

        Logger consoleLogger = consoleFactory.create();
        consoleLogger.log("Lambda 工厂创建");

        Logger fileLogger = fileFactory.create();
        fileLogger.log("Lambda 工厂创建文件日志");
    }
}

4.3 生产级实现(Spring Boot 场景)

java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 生产级支付服务 —— 结合 Spring Boot 的工厂方法模式
 *
 * 业务场景:多渠道支付(微信支付、支付宝、银联支付、PayPal)
 * 新增支付渠道只需添加新的 PaymentService 实现类,无需修改框架代码
 */

// ========== 1. 支付策略接口(抽象产品) ==========
interface PaymentService {
    /** 支付类型标识 */
    String getType();
    /** 执行支付 */
    PayResult pay(PayRequest request);
    /** 退款 */
    RefundResult refund(RefundRequest request);
}

// ========== 2. 请求/响应对象 ==========
class PayRequest {
    private String orderId;
    private double amount;
    private String currency;

    public PayRequest(String orderId, double amount) {
        this.orderId = orderId;
        this.amount = amount;
    }
    public String getOrderId() { return orderId; }
    public double getAmount() { return amount; }
}

class PayResult {
    private boolean success;
    private String transactionId;
    private String message;

    public PayResult(boolean success, String transactionId, String message) {
        this.success = success;
        this.transactionId = transactionId;
        this.message = message;
    }
    @Override
    public String toString() {
        return "PayResult{success=" + success + ", txnId='" + transactionId + "', msg='" + message + "'}";
    }
}

class RefundRequest {
    private String transactionId;
    private double amount;
    public RefundRequest(String transactionId, double amount) {
        this.transactionId = transactionId;
        this.amount = amount;
    }
}

class RefundResult {
    private boolean success;
    public RefundResult(boolean success) { this.success = success; }
}

// ========== 3. 具体支付实现(具体产品) ==========
@Service("wechatPay")
class WeChatPayService implements PaymentService {
    @Override
    public String getType() { return "WECHAT"; }

    @Override
    public PayResult pay(PayRequest request) {
        System.out.println("[微信支付] 订单: " + request.getOrderId() + ", 金额: " + request.getAmount());
        // 实际调用微信支付 API
        return new PayResult(true, "WX_" + System.currentTimeMillis(), "微信支付成功");
    }

    @Override
    public RefundResult refund(RefundRequest request) {
        System.out.println("[微信支付] 退款: " + request.getTransactionId());
        return new RefundResult(true);
    }
}

@Service("alipay")
class AliPayService implements PaymentService {
    @Override
    public String getType() { return "ALIPAY"; }

    @Override
    public PayResult pay(PayRequest request) {
        System.out.println("[支付宝] 订单: " + request.getOrderId() + ", 金额: " + request.getAmount());
        return new PayResult(true, "ALI_" + System.currentTimeMillis(), "支付宝支付成功");
    }

    @Override
    public RefundResult refund(RefundRequest request) {
        System.out.println("[支付宝] 退款: " + request.getTransactionId());
        return new RefundResult(true);
    }
}

@Service("unionPay")
class UnionPayService implements PaymentService {
    @Override
    public String getType() { return "UNION"; }

    @Override
    public PayResult pay(PayRequest request) {
        System.out.println("[银联支付] 订单: " + request.getOrderId() + ", 金额: " + request.getAmount());
        return new PayResult(true, "UN_" + System.currentTimeMillis(), "银联支付成功");
    }

    @Override
    public RefundResult refund(RefundRequest request) {
        System.out.println("[银联支付] 退款: " + request.getTransactionId());
        return new RefundResult(true);
    }
}

// ========== 4. 支付工厂(Spring 自动注入所有实现) ==========
@Component
class PaymentFactory {

    /**
     * Spring 自动将所有 PaymentService 实现注入到 Map 中
     * key = Bean 名称,value = Bean 实例
     */
    private final Map<String, PaymentService> paymentServiceMap;

    @Autowired
    public PaymentFactory(List<PaymentService> paymentServices) {
        // 将 List 转为 Map,key 为支付类型
        this.paymentServiceMap = paymentServices.stream()
                .collect(Collectors.toMap(PaymentService::getType, Function.identity()));
        System.out.println("[PaymentFactory] 已加载支付渠道: " + paymentServiceMap.keySet());
    }

    /**
     * 根据支付类型获取对应的支付服务
     */
    public PaymentService getPaymentService(String payType) {
        PaymentService service = paymentServiceMap.get(payType.toUpperCase());
        if (service == null) {
            throw new IllegalArgumentException("不支持的支付类型: " + payType);
        }
        return service;
    }
}

// ========== 5. 业务层使用工厂 ==========
@Service
class OrderService {

    @Autowired
    private PaymentFactory paymentFactory;

    public PayResult createOrder(String orderId, double amount, String payType) {
        // 通过工厂获取支付服务,客户端无需知道具体实现
        PaymentService paymentService = paymentFactory.getPaymentService(payType);
        PayRequest request = new PayRequest(orderId, amount);
        return paymentService.pay(request);
    }

    // ========== 测试入口 ==========
    public static void main(String[] args) {
        // 模拟 Spring 容器手动注入
        List<PaymentService> services = List.of(
                new WeChatPayService(), new AliPayService(), new UnionPayService()
        );
        PaymentFactory factory = new PaymentFactory(services);
        OrderService orderService = new OrderService();
        // 手动注入 factory(模拟 @Autowired)
        orderService.paymentFactory = factory;

        // 测试不同支付方式
        System.out.println(orderService.createOrder("ORDER-001", 99.99, "WECHAT"));
        System.out.println(orderService.createOrder("ORDER-002", 199.99, "ALIPAY"));
        System.out.println(orderService.createOrder("ORDER-003", 299.99, "UNION"));
    }
}

五、优缺点

优点

优点说明
符合开闭原则新增产品只需添加新的工厂和产品类,无需修改已有代码
符合单一职责原则产品创建逻辑集中在工厂中,与业务逻辑分离
解耦客户端只依赖抽象接口,不依赖具体产品类
可扩展性强轻松扩展新产品类型

缺点

缺点说明
类数量增加每增加一个产品就需要增加一个工厂类,可能导致类爆炸
增加系统复杂度相比简单工厂,引入了更多抽象层
客户端需要知道具体工厂仍需选择具体工厂,只是把耦合从产品移到了工厂

六、适用场景

  1. 日志框架:支持多种输出方式(控制台、文件、数据库、远程)
  2. 支付系统:多渠道支付(微信、支付宝、银联、PayPal)
  3. 消息中间件:同一接口适配不同消息队列(Kafka、RabbitMQ、RocketMQ)
  4. 数据库连接:不同数据库厂商(MySQL、Oracle、PostgreSQL)
  5. 文件导出:不同格式导出(PDF、Excel、CSV、Word)
  6. UI 组件库:跨平台 UI 组件(按钮、文本框在不同操作系统下的样式)
  7. 通知服务:多渠道通知(短信、邮件、APP Push、企业微信)

七、JDK / Spring 框架中的实际应用

7.1 JDK 中的工厂方法

1. java.util.Calendar.getInstance()
   - 根据 Locale 返回不同的 Calendar 实现(GregorianCalendar、JapaneseImperialCalendar 等)

2. java.text.NumberFormat.getInstance()
   - 根据 Locale 返回不同的数字格式化器

3. java.util.ResourceBundle.getBundle()
   - 根据 Locale 加载不同的资源文件

4. java.nio.charset.Charset.forName()
   - 根据字符集名称返回对应的 Charset 实现

5. java.net.URLStreamHandlerFactory
   - 创建不同协议的 URL 流处理器

7.2 Spring 框架中的工厂方法

1. BeanFactory(核心工厂)
   - Spring IoC 容器的顶层接口,定义了 getBean() 工厂方法
   - ApplicationContext 继承自 BeanFactory

2. FactoryBean<T>
   - Spring 提供的特殊工厂 Bean 接口
   - getObject() 方法就是工厂方法
   - 如 SqlSessionFactoryBean、ProxyFactoryBean

3. ObjectFactory<T>
   - 函数式工厂接口,常与依赖注入结合使用

4. AbstractApplicationContext.getBeanFactory()
   - 返回 DefaultListableBeanFactory 实例

7.3 源码示例

java
// Spring 中的 FactoryBean 接口(简化版)
public interface FactoryBean<T> {
    // 工厂方法:返回创建的 Bean 实例
    T getObject() throws Exception;

    // 返回 Bean 类型
    Class<?> getObjectType();

    // 是否为单例
    default boolean isSingleton() {
        return true;
    }
}

// MyBatis 中的 SqlSessionFactoryBean 使用示例
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory> {
    @Override
    public SqlSessionFactory getObject() throws Exception {
        // 复杂的创建逻辑:解析 XML 配置、构建 Configuration、创建 SqlSessionFactory
        return new SqlSessionFactoryBuilder().build(configuration);
    }
}

八、与其他模式的关系

相关模式关系说明
抽象工厂模式工厂方法只有一个抽象产品,抽象工厂有多个抽象产品(产品族)。抽象工厂经常用工厂方法来实现
简单工厂模式工厂方法的前身,只有一个工厂类,通过参数区分产品。工厂方法将工厂也抽象化了
单例模式工厂类通常被设计为单例,避免重复创建工厂对象
模板方法模式工厂方法常配合模板方法使用:父类定义算法骨架,工厂方法作为步骤之一由子类实现
策略模式工厂方法创建策略对象,策略模式定义算法族。两者经常组合使用

九、面试常见问题

Q1:简单工厂、工厂方法、抽象工厂的区别?

答案

维度简单工厂工厂方法抽象工厂
工厂数量1 个多个(每种产品一个)多个(每个产品族一个)
产品数量多种产品一种产品多种产品(产品族)
扩展方式修改工厂类新增工厂类新增工厂类
开闭原则违反符合符合
复杂度

Q2:工厂方法模式中,工厂类为什么要设计为抽象类而不是接口?

答案: 工厂类可以是接口也可以是抽象类,各有适用场景:

  • 接口:当工厂除了创建产品外没有其他逻辑时,用接口更灵活
  • 抽象类:当工厂有一些公共逻辑(如日志、参数校验、缓存)时,抽象类更好。可以在抽象类中定义 templateMethod(),调用 factoryMethod() 完成模板方法模式

Q3:Spring 中 @Autowired 注入 List<Interface> 是如何实现工厂方法模式的?

答案: Spring 会自动收集所有实现了 Interface 的 Bean,注入到一个 List 中。这是一种"隐式工厂方法"的实现:

java
@Autowired
private List<PaymentService> paymentServices; // Spring 自动注入所有实现

// 配合 Map 可以按类型查找
@Autowired
private Map<String, PaymentService> paymentServiceMap; // key=beanName, value=bean

这种方式比传统工厂方法更优雅,新增实现类只需加 @Service 注解,无需修改工厂代码。


Q4:工厂方法模式和策略模式有什么区别?

答案

  • 目的不同:工厂方法关注创建对象,策略模式关注行为选择
  • 关注点不同:工厂方法返回产品对象给客户端使用,策略模式封装算法让客户端切换行为
  • 组合使用:两者经常组合 —— 工厂方法创建策略对象,策略模式封装具体算法

Q5:为什么说工厂方法模式符合"依赖倒置原则"?

答案: 依赖倒置原则(DIP)要求:高层模块不应依赖低层模块,二者都应依赖抽象。

  • 工厂方法中,客户端依赖 LoggerFactory 接口(抽象),不依赖 ConsoleLoggerFactory(具体)
  • 客户端依赖 Logger 接口(抽象),不依赖 ConsoleLogger(具体)
  • 具体工厂和具体产品都依赖抽象接口,完美符合 DIP

总结

工厂方法模式是创建型设计模式中最常用的模式之一,核心思想是将对象的创建与使用分离。实际开发中:

  • 简单场景用简单工厂即可
  • 需要扩展时升级为工厂方法
  • Spring 项目中利用 Spring 自动注入 List<Interface> 是最优雅的工厂方法实现
  • 工厂方法常与单例模式(工厂对象为单例)、策略模式(工厂创建策略对象)组合使用