以下是为你精心撰写的 《Java 工厂方法模式深度学习指南》,专为 Java 后端开发者设计,内容涵盖:定义、作用、真实业务场景、实现方式对比、Spring 中的体现、避坑指南、最佳实践、面试高频题,所有示例均含中文注释,可直接用于项目开发、重构与面试准备。
📘 Java 工厂方法模式深度学习指南
—— 从“new”到“工厂”的架构进化之路
作者:Java 后端架构实战导师
适用对象:Java 后端开发者(Spring Boot / 微服务 / 分布式系统)
目标:彻底理解工厂方法模式,摆脱“硬编码 new”,实现开闭原则和解耦设计
核心原则:“创建对象的责任,交给子类”
✅ 一、什么是工厂方法模式?
📌 定义:
工厂方法模式(Factory Method Pattern) 是一种创建型设计模式,它定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法让一个类的实例化延迟到其子类中进行。
🔍 核心思想:
- 定义一个创建对象的抽象方法(工厂方法)
- 由子类实现该方法,决定具体创建哪个类
- 客户端只依赖抽象接口,不依赖具体实现
💡 一句话记忆:
“我要一个产品,但不关心你怎么造,你(子类)来决定造什么。”
🆚 对比简单工厂 vs 工厂方法:
| 模式 | 简单工厂 | 工厂方法 |
|---|---|---|
| 实现方式 | 一个静态方法,用 if-else 判断 | 抽象类定义工厂方法,子类重写 |
| 是否符合开闭原则 | ❌ 新增产品需修改工厂代码 | ✅ 新增产品只需新增子类,不改旧代码 |
| 扩展性 | 差 | 优秀 |
| 耦合度 | 高(工厂类知道所有产品) | 低(客户端只依赖抽象) |
| 推荐度 | 仅用于简单场景 | ✅ 推荐用于中大型系统 |
✅ 结论:工厂方法模式是“简单工厂”的升级版,是真正的面向对象设计!
✅ 二、工厂方法模式有什么作用?(Why Use Factory Method?)
| 作用 | 说明 |
|---|---|
| ✅ 解耦对象创建与使用 | 客户端无需知道具体类名,只调用接口 |
| ✅ 支持开闭原则(OCP) | 新增产品类型时,无需修改现有代码,只需新增子类 |
| ✅ 提高可扩展性 | 易于支持多种产品变体(如不同支付方式、不同日志格式) |
| ✅ 统一接口,规范行为 | 所有产品实现同一接口,客户端行为一致 |
| ✅ 便于测试 | 可轻松用 Mock 对象替换真实实现 |
| ✅ 支持依赖注入(DI) | 与 Spring 容器完美融合 |
💡 经典比喻:
你去餐厅点“汉堡”——你只说“我要一个汉堡”,
不关心是麦当劳的、肯德基的,还是本地小店的。
餐厅(工厂) 负责根据“菜单”决定做哪个。
→ 这就是工厂方法!
✅ 三、工厂方法模式的典型使用场景(Java 后端真实案例)
| 场景 | 说明 | 是否推荐使用工厂方法 |
|---|---|---|
| ✅ 支付方式 | 支付宝、微信、银联、Apple Pay | ✅ 强烈推荐 |
| ✅ 日志输出 | 控制台、文件、数据库、ELK | ✅ 推荐 |
| ✅ 消息发送 | 短信、邮件、站内信、企业微信 | ✅ 推荐 |
| ✅ 数据库驱动 | MySQL、PostgreSQL、Oracle | ✅ 推荐 |
| ✅ 序列化方式 | JSON、XML、Protobuf | ✅ 推荐 |
| ✅ 缓存实现 | Redis、Memcached、本地缓存 | ✅ 推荐 |
| ✅ 文件读取器 | CSV、Excel、JSON 文件解析器 | ✅ 推荐 |
❌ 工具类(如 StringUtils) | 无状态,静态方法即可 | ❌ 不推荐 |
❌ 简单对象(如 User、Order) | 通常直接 new,无必要封装 | ❌ 不推荐 |
✅ 判断标准:
“是否需要根据配置/环境/参数动态创建不同实现?”
→ 是 → 用工厂方法!
✅ 四、工厂方法模式的四种实现方式详解(含中文注释)
我们从基础结构到Spring 集成,逐步深入。
🔹 1. 基础结构:抽象产品 + 抽象工厂 + 具体产品 + 具体工厂
📌 步骤:
- 定义产品接口(抽象产品)
- 定义工厂接口(抽象工厂)
- 实现具体产品类
- 实现具体工厂类
✅ 示例:支付系统(支付宝、微信、银联)
/**
* 【1】抽象产品:支付接口(所有支付方式的统一契约)
*/
interface Payment {
boolean pay(double amount); // 支付方法
}
/**
* 【2】具体产品1:支付宝支付实现
*/
class Alipay implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("💳 支付宝支付成功,金额:" + amount + "元");
return true;
}
}
/**
* 【3】具体产品2:微信支付实现
*/
class WeChatPay implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("📱 微信支付成功,金额:" + amount + "元");
return true;
}
}
/**
* 【4】抽象工厂:支付工厂接口(定义创建产品的抽象方法)
*/
interface PaymentFactory {
Payment createPayment(); // 工厂方法:由子类决定创建哪个支付方式
}
/**
* 【5】具体工厂1:支付宝工厂
*/
class AlipayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new Alipay(); // 创建支付宝对象
}
}
/**
* 【6】具体工厂2:微信工厂
*/
class WeChatPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new WeChatPay(); // 创建微信支付对象
}
}
/**
* 【7】客户端:调用者(只依赖抽象,不关心具体实现)
*/
public class PaymentClient {
public static void main(String[] args) {
// 模拟从配置文件读取支付方式
String paymentType = "alipay";
PaymentFactory factory;
if ("alipay".equals(paymentType)) {
factory = new AlipayFactory(); // 创建具体工厂
} else if ("wechat".equals(paymentType)) {
factory = new WeChatPayFactory();
} else {
throw new IllegalArgumentException("不支持的支付方式");
}
// ✅ 客户端只调用抽象工厂和抽象产品
Payment payment = factory.createPayment(); // 调用工厂方法
payment.pay(199.9); // 执行支付
// 输出:💳 支付宝支付成功,金额:199.9元
}
}
✅ 优点:
- 完全符合开闭原则:新增“银联支付”只需新增
UnionPay和UnionPayFactory,无需修改任何已有代码 - 客户端解耦:客户端不知道具体类名,只依赖接口
- 易于测试:可 Mock
PaymentFactory返回 MockPayment
💡 为什么叫“方法”?
因为“创建对象”是通过一个方法(
createPayment())完成的,而不是通过new关键字。
🔹 2. 工厂方法 + 工厂注册表(Map + 反射)—— 更灵活的实现
避免在客户端写
if-else判断,把选择权交给配置
import java.util.HashMap;
import java.util.Map;
/**
* 【1】支付接口(同上)
*/
interface Payment {
boolean pay(double amount);
}
class Alipay implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("💳 支付宝支付成功,金额:" + amount + "元");
return true;
}
}
class WeChatPay implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("📱 微信支付成功,金额:" + amount + "元");
return true;
}
}
/**
* 【2】工厂注册表:使用 Map 存储工厂类名(避免硬编码)
*/
class PaymentFactoryRegistry {
// 注册表:支付类型 -> 工厂类名
private static final Map<String, Class<? extends PaymentFactory>> factoryMap = new HashMap<>();
// 静态块:初始化注册表(可从配置文件读取)
static {
factoryMap.put("alipay", AlipayFactory.class);
factoryMap.put("wechat", WeChatPayFactory.class);
}
/**
* 根据支付类型获取工厂实例
*/
public static PaymentFactory getFactory(String type) {
try {
Class<? extends PaymentFactory> factoryClass = factoryMap.get(type);
if (factoryClass == null) {
throw new IllegalArgumentException("未知支付类型:" + type);
}
return factoryClass.getDeclaredConstructor().newInstance(); // 反射创建
} catch (Exception e) {
throw new RuntimeException("创建工厂失败", e);
}
}
}
/**
* 【3】抽象工厂接口(同上)
*/
interface PaymentFactory {
Payment createPayment();
}
/**
* 【4】具体工厂类(同上)
*/
class AlipayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new Alipay();
}
}
class WeChatPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new WeChatPay();
}
}
/**
* 【5】客户端:完全解耦,仅依赖配置
*/
public class RegistryFactoryDemo {
public static void main(String[] args) {
// 从配置文件或环境变量读取
String paymentType = "alipay"; // 可从 application.yml 读取
// ✅ 无需 if-else,通过注册表自动获取工厂
PaymentFactory factory = PaymentFactoryRegistry.getFactory(paymentType);
Payment payment = factory.createPayment();
payment.pay(299.9);
// 输出:💳 支付宝支付成功,金额:299.9元
}
}
✅ 优点:
- 新增支付方式:只需在
factoryMap添加一行,无需修改客户端代码 - 支持热加载:可从数据库或配置中心动态加载工厂类名
- 真正符合开闭原则
⚠️ 注意:
- 使用反射有性能开销,可缓存工厂实例
- 非必要不推荐,适合大型可插拔系统
🔹 3. 工厂方法 + Spring 容器(最推荐的生产级写法)
在 Spring Boot 中,工厂方法模式是默认模式!
/**
* 【1】支付接口(抽象产品)
*/
public interface Payment {
boolean pay(double amount);
}
/**
* 【2】具体产品1:支付宝支付
*/
@Component("alipay") // ✅ Spring 管理的 Bean,名称为 "alipay"
public class Alipay implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("💳 支付宝支付成功,金额:" + amount + "元");
return true;
}
}
/**
* 【3】具体产品2:微信支付
*/
@Component("wechat") // ✅ Spring 管理的 Bean,名称为 "wechat"
public class WeChatPay implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("📱 微信支付成功,金额:" + amount + "元");
return true;
}
}
/**
* 【4】工厂类:Spring 管理的工厂(使用 @Bean + @Qualifier)
* 这是工厂方法模式在 Spring 中的优雅体现!
*/
@Configuration
public class PaymentFactoryConfig {
// ✅ 工厂方法:根据参数返回不同 Bean
@Bean
public Payment createPayment(@Value("${payment.type:alipay}") String paymentType) {
// Spring 自动注入所有 Payment 类型的 Bean
// 通过 @Qualifier 按名称获取
return switch (paymentType.toLowerCase()) {
case "alipay" -> applicationContext.getBean("alipay", Payment.class);
case "wechat" -> applicationContext.getBean("wechat", Payment.class);
default -> throw new IllegalArgumentException("不支持的支付方式:" + paymentType);
};
}
@Autowired
private ApplicationContext applicationContext; // 注入 Spring 上下文
}
/**
* 【5】服务层:使用工厂
*/
@Service
public class OrderService {
@Autowired
private Payment payment; // ✅ Spring 自动注入正确实现(由工厂方法决定)
public void processOrder(double amount) {
System.out.println("🛒 开始处理订单,金额:" + amount);
boolean result = payment.pay(amount); // 调用支付
if (result) {
System.out.println("✅ 订单支付成功");
}
}
}
/**
* 【6】配置文件:application.yml
*/
/*
payment:
type: wechat # 可改为 alipay,无需改代码!
*/
/**
* 【7】启动类测试
*/
@SpringBootApplication
public class SpringFactoryDemo {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringFactoryDemo.class, args);
OrderService orderService = context.getBean(OrderService.class);
orderService.processOrder(188.8);
// 输出:
// 🛒 开始处理订单,金额:188.8
// 📱 微信支付成功,金额:188.8元
// ✅ 订单支付成功
}
}
✅ 优点:
- 完全解耦:客户端(
OrderService)不知道具体实现 - 配置驱动:通过
application.yml动态切换支付方式 - 无需手写工厂类:Spring 自动管理 Bean 生命周期
- 支持 AOP、事务、缓存:所有 Spring 功能自动生效
💡 这是真正的“工厂方法模式”在企业级开发中的体现!
✅ 你每天都在用:
@Autowired JdbcTemplate@Autowired MessageConverter@Autowired TaskExecutor
→ 都是 Spring 工厂方法模式的体现!
🔹 4. 工厂方法 + 枚举 + 策略模式(企业级最佳实践)
将工厂方法与枚举结合,实现类型安全、零反射、易扩展
/**
* 【1】支付接口
*/
interface Payment {
boolean pay(double amount);
}
/**
* 【2】具体实现
*/
class Alipay implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("💳 支付宝支付成功,金额:" + amount + "元");
return true;
}
}
class WeChatPay implements Payment {
@Override
public boolean pay(double amount) {
System.out.println("📱 微信支付成功,金额:" + amount + "元");
return true;
}
}
/**
* 【3】支付方式枚举(核心:工厂方法嵌入枚举)
* 每个枚举常量是一个“工厂”,返回对应支付实例
*/
public enum PaymentType {
ALIPAY {
@Override
public Payment create() {
return new Alipay();
}
},
WECHAT {
@Override
public Payment create() {
return new WeChatPay();
}
};
// 抽象工厂方法:每个枚举实现自己的创建逻辑
public abstract Payment create();
// 静态方法:根据字符串获取枚举
public static PaymentType fromString(String type) {
for (PaymentType pt : values()) {
if (pt.name().equalsIgnoreCase(type)) {
return pt;
}
}
throw new IllegalArgumentException("未知支付类型:" + type);
}
}
/**
* 【4】客户端:极简调用
*/
public class EnumFactoryDemo {
public static void main(String[] args) {
String paymentType = "alipay";
Payment payment = PaymentType.fromString(paymentType).create(); // ✅ 一行创建
payment.pay(99.9);
// 输出:💳 支付宝支付成功,金额:99.9元
}
}
✅ 优点:
- 类型安全:编译期检查,避免拼写错误
- 无反射:性能高
- 防非法值:枚举值有限,不可伪造
- 可序列化:枚举天然支持
- 代码极简
💡 推荐场景:
支付、协议、状态、权限类型等有限且稳定的枚举场景
✅ 五、工厂方法模式的避坑指南(Java 后端高频踩坑)
| 问题 | 原因 | 解决方案 |
|---|---|---|
❌ 用 new 直接创建对象 | 耦合严重,无法扩展 | ✅ 改为通过工厂方法获取 |
❌ 工厂类中用 if-else 判断类型 | 违反开闭原则 | ✅ 用 Map + 配置 或 枚举 |
| ❌ 工厂类依赖具体实现类 | 导致“工厂膨胀” | ✅ 所有产品实现同一接口 |
| ❌ 忘记使用抽象接口 | 客户端依赖具体类 | ✅ 所有变量声明为接口类型:Payment payment = factory.create(); |
❌ 在 Spring 中手写工厂而不用 @Bean | 重复造轮子 | ✅ 优先用 Spring 容器管理 |
| ❌ 多线程下工厂创建对象不安全 | 若对象有状态 | ✅ 确保产品是无状态或线程安全的 |
✅ 六、工厂方法模式 vs 其他模式对比
| 模式 | 区别 | 适用场景 |
|---|---|---|
| 工厂方法 | 抽象工厂定义方法,子类决定实现 | 一个产品族,多个实现(如支付) |
| 抽象工厂 | 创建多个产品族(如 UI 主题 + 按钮+菜单) | 多套完整产品组合(如 Windows/Mac 主题) |
| 简单工厂 | 一个类包含所有创建逻辑 | 小型项目,无扩展需求 |
| 建造者 | 分步构建复杂对象 | 构建有多个参数的对象(如 SQL、DTO) |
| 策略模式 | 算法可互换 | 同一接口不同行为(如排序、折扣) |
✅ 记住:
工厂方法 = “我需要一个产品,你来决定造哪个”
策略模式 = “我需要一个算法,你来决定用哪个”
✅ 七、学习建议与进阶路径
| 阶段 | 建议 |
|---|---|
| 📚 第一周 | 在你的项目中,把所有 new Alipay() 替换为 PaymentFactory.getPayment("alipay") |
| 📚 第二周 | 用 Spring 的 @Component + @Value 实现支付方式动态切换 |
| 📚 第三周 | 用枚举工厂重构“日志输出方式”(控制台/文件/数据库) |
| 📚 第四周 | 阅读 Spring 源码:BeanFactory.getBean() 如何实现工厂方法? |
| 📚 面试准备 | 准备回答: |
“你项目中哪里用了工厂方法?”
“Spring 中的 @Bean 是工厂方法吗?”
“工厂方法和策略模式区别?”
“为什么不用简单工厂?” |
✅ 八、总结:工厂方法模式选型决策树
graph TD
A[需要创建不同对象吗?] --> B{对象类型是否有限且稳定?}
B -->|是| C[使用枚举工厂(PaymentType.ALIPAY.create())]
B -->|否| D{是否在 Spring 项目中?}
D -->|是| E[使用 @Component + @Value + @Autowired,让 Spring 管理]
D -->|否| F[使用抽象工厂接口 + 具体工厂类]
G[是否需要热插拔?] --> H[使用 Map + 配置文件 + 反射]
✅ 最终推荐:
- Spring 项目 → ✅ @Component + @Value(最优雅)
- 非 Spring、类型固定 → ✅ 枚举工厂(最安全)
- 非 Spring、需扩展 → ✅ 抽象工厂 + 具体工厂(最标准)
- 绝对不要:简单工厂(if-else)、手写 new
✅ 九、工厂方法模式面试高频题(附答案)
Q1:什么是工厂方法模式?和简单工厂有什么区别?
A:工厂方法定义一个创建对象的接口,由子类决定实例化哪个类;简单工厂是一个类用 if-else 统一创建,违反开闭原则。
Q2:Spring 中的 @Bean 是工厂方法吗?
A:是的!
@Bean方法就是工厂方法,Spring 容器调用它来创建 Bean。
Q3:工厂方法模式如何实现开闭原则?
A:新增产品时,只需新增具体产品类和具体工厂类,无需修改客户端或抽象工厂代码。
Q4:工厂方法模式适合什么场景?
A:需要根据配置/环境创建不同实现的场景,如支付、日志、序列化、数据库驱动。
Q5:工厂方法模式和策略模式的区别?
A:
- 工厂方法:创建对象(谁来造?)
- 策略模式:执行行为(怎么干?)
举例:工厂创建“支付宝”对象,策略决定“用支付宝支付”还是“用微信支付”。
✅ 十、结语:真正的高手,不写 new,而写 @Bean
你不是在学“工厂方法”,你是在学“如何让系统不因变化而崩溃”。
✅ 当你看到
@Autowired Payment payment时,你已经站在了工厂方法的巅峰。
✅ 当你用application.yml切换支付方式而不改一行代码时,你已经掌握了开闭原则。
不要为了“用模式”而用模式,
要为了“系统可扩展、可维护、可配置”而选择它。
879

被折叠的 条评论
为什么被折叠?



