Appearance
工厂方法模式 (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"));
}
}五、优缺点
优点
| 优点 | 说明 |
|---|---|
| 符合开闭原则 | 新增产品只需添加新的工厂和产品类,无需修改已有代码 |
| 符合单一职责原则 | 产品创建逻辑集中在工厂中,与业务逻辑分离 |
| 解耦 | 客户端只依赖抽象接口,不依赖具体产品类 |
| 可扩展性强 | 轻松扩展新产品类型 |
缺点
| 缺点 | 说明 |
|---|---|
| 类数量增加 | 每增加一个产品就需要增加一个工厂类,可能导致类爆炸 |
| 增加系统复杂度 | 相比简单工厂,引入了更多抽象层 |
| 客户端需要知道具体工厂 | 仍需选择具体工厂,只是把耦合从产品移到了工厂 |
六、适用场景
- 日志框架:支持多种输出方式(控制台、文件、数据库、远程)
- 支付系统:多渠道支付(微信、支付宝、银联、PayPal)
- 消息中间件:同一接口适配不同消息队列(Kafka、RabbitMQ、RocketMQ)
- 数据库连接:不同数据库厂商(MySQL、Oracle、PostgreSQL)
- 文件导出:不同格式导出(PDF、Excel、CSV、Word)
- UI 组件库:跨平台 UI 组件(按钮、文本框在不同操作系统下的样式)
- 通知服务:多渠道通知(短信、邮件、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>是最优雅的工厂方法实现 - 工厂方法常与单例模式(工厂对象为单例)、策略模式(工厂创建策略对象)组合使用
