目录
[1. 定义与结构](#1. 定义与结构)
[2. Java代码实战](#2. Java代码实战)
[3. 优缺点分析](#3. 优缺点分析)
[1. 定义与结构](#1. 定义与结构)
[2. Java代码实战](#2. Java代码实战)
[3. 优缺点分析](#3. 优缺点分析)
[1. 简单工厂 vs 工厂方法:选择指南](#1. 简单工厂 vs 工厂方法:选择指南)
[2. 现代 Java 开发的"第三条路":Spring 依赖注入 (DI)](#2. 现代 Java 开发的“第三条路”:Spring 依赖注入 (DI))
摘要 :工厂模式是Java开发中最经典、使用频率最高的创建型设计模式之一。很多初学者容易陷入"为了模式而模式"的误区,导致代码过度设计。本文将通过清晰的Java代码示例,深入浅出地讲解简单工厂 与工厂方法 的核心区别,并结合Spring生态下的最佳实践,告诉你到底该在什么场景下用哪种工厂,助你写出既优雅又实用的代码。
一、为什么我们需要工厂模式?
在传统的面向对象编程中,我们习惯直接使用
new关键字来创建对象:
java
// 传统方式
Car myCar = new BenzCar();
myCar.drive();
这种方式虽然简单直接,但随着系统复杂度的提升,弊端逐渐显现:
- 耦合度高 :调用者依赖具体类(
BenzCar),一旦类名变更或构造逻辑变化,所有new的地方都要修改。- 违反开闭原则 :如果要增加新产品(如
TeslaCar),必须修改原有业务逻辑。- 创建逻辑分散:如果对象创建需要复杂的校验、配置或日志记录,这些逻辑会散落在各个业务代码中,难以维护。
工厂模式的核心思想 就是:将对象的创建逻辑封装起来,让调用者只关心"用什么",而不关心"怎么造"。
这就好比去餐厅点餐,你只需要告诉服务员"我要一份牛排",至于牛排是哪个厨师做的、怎么切的、怎么煎的,你不需要知道,服务员(工厂)会直接把做好的牛排(对象)端给你。
二、核心套路一:简单工厂 (Simple Factory)
1. 定义与结构
简单工厂(也称为静态工厂方法)是最基础的工厂模式。它创建一个专门的工厂类,根据传入的参数(如字符串、枚举)决定创建哪一种产品类的实例(看一遍就懂,很好理解)。
2. Java代码实战
假设我们要开发一个图形绘制模块,支持圆形和方形。
java
// 1. 定义产品接口
interface Shape {
void draw();
}
// 2. 具体产品实现
class Circle implements Shape {
@Override
public void draw() {
System.out.println("画了一个圆形 ⭕");
}
}
class Square implements Shape {
@Override
public void draw() {
System.out.println("画了一个方形 ⬜");
}
}
// 3. 简单工厂类
class ShapeFactory {
// 使用静态方法,无需实例化工厂即可调用
public static Shape createShape(String type) {
if ("circle".equalsIgnoreCase(type)) {
return new Circle();
} else if ("square".equalsIgnoreCase(type)) {
return new Square();
} else {
throw new IllegalArgumentException("未知的形状类型: " + type);
}
}
}
// 4. 客户端调用
public class SimpleFactoryDemo {
public static void main(String[] args) {
// 客户端只依赖接口 Shape 和工厂类,不依赖具体实现类
Shape s1 = ShapeFactory.createShape("circle");
s1.draw();
Shape s2 = ShapeFactory.createShape("square");
s2.draw();
}
}
3. 优缺点分析
- 优点 :
- 结构简单,易于理解。
- 客户端免除了直接创建产品的责任,实现了责任分离。
- 缺点 :
- 违反开闭原则 :如果要增加一个
Triangle(三角形),必须修改ShapeFactory中的if-else逻辑。在大型系统中,这可能导致工厂类变得臃肿且难以维护。
三、核心套路二:工厂方法 (Factory Method)
1. 定义与结构
工厂方法模式定义了一个创建对象的接口(抽象工厂),但让实现这个接口的子类来决定实例化哪一个类。工厂方法让类的实例化推迟到子类。
2. Java代码实战
假设我们要构建一个物流系统,支持陆运(卡车)和海运(轮船),且未来可能扩展空运。
java
// 1. 定义产品接口
interface Transport {
void deliver();
}
// 2. 具体产品实现
class Truck implements Transport {
@Override
public void deliver() {
System.out.println("🚛 卡车通过公路运输货物");
}
}
class Ship implements Transport {
@Override
public void deliver() {
System.out.println("🚢 轮船通过海运运输货物");
}
}
// 3. 抽象工厂 (Creator)
// 这里定义了业务逻辑框架,但把具体的创建过程留给子类
abstract class Logistics {
// 工厂方法:由子类实现
public abstract Transport createTransport();
// 模板方法:包含通用业务逻辑
public void planDelivery() {
Transport transport = createTransport(); // 调用工厂方法
System.out.print("物流计划启动:");
transport.deliver();
}
}
// 4. 具体工厂实现
class RoadLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Truck();
}
}
class SeaLogistics extends Logistics {
@Override
public Transport createTransport() {
return new Ship();
}
}
// 5. 客户端调用
public class FactoryMethodDemo {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Logistics roadLogistics = new RoadLogistics();
roadLogistics.planDelivery();
Logistics seaLogistics = new SeaLogistics();
seaLogistics.planDelivery();
// 【扩展性体现】:如果需要空运,只需新增 AirLogistics 和 Airplane 类,
// 现有的 Logistics, RoadLogistics, SeaLogistics 代码一行都不用改!
}
}
3. 优缺点分析
- 优点 :
- 符合开闭原则:增加新产品时,只需新增对应的具体工厂和产品类,无需修改现有代码。
- 解耦彻底:高层模块只依赖抽象工厂和抽象产品。
- 缺点 :
- 类个数膨胀:每增加一个产品,就需要增加一个产品类和一个工厂类,系统复杂度增加。
- 抽象性增加:增加了系统的理解难度。
四、灵魂拷问:实际开发中到底用哪个?
这是很多开发者最纠结的问题。答案并非非黑即白,而是取决于场景。
1. 简单工厂 vs 工厂方法:选择指南
| 维度 | 简单工厂 (Simple Factory) | 工厂方法 (Factory Method) |
|---|---|---|
| 使用频率 | ⭐⭐⭐⭐ (高频) | ⭐⭐ (中低频,多用于框架底层) |
| 代码复杂度 | 低 | 高 (类数量多) |
| 扩展性 | 一般 (需修改代码) | 极好 (无需修改代码) |
| 适用场景 | 工具类、策略选择、小型模块、产品类型固定 | 框架设计、插件系统、SDK、产品类型频繁变动 |
结论:
- 日常业务开发 :90% 的场景下,简单工厂(或其变种"静态工厂方法")完全够用。因为业务类型通常相对固定,为了扩展性牺牲代码简洁度是不划算的。
- 框架/库开发 :如果你是在写一个供他人扩展的框架(如 JDBC 驱动、日志框架),工厂方法是标准答案,因为它能完美支持第三方插件的无缝接入。
2. 现代 Java 开发的"第三条路":Spring 依赖注入 (DI)
在 Spring Boot 统治的企业级开发中,我们其实很少手动写这两种工厂了。
为什么?
因为 Spring 容器 已经帮你实现了终极版的"抽象工厂 + 简单工厂"。
-
以前(手动工厂):
java// 痛苦地写 if-else 或者继承 Service s = Factory.getService("mysql"); -
现在(Spring DI):
java@Autowired private PaymentService paymentService; // Spring 自动帮你 new 好了 // 或者通过构造函数注入 public OrderController(PaymentService paymentService) { this.paymentService = paymentService; }
在 Spring 中,你只需要定义接口和实现类,加上
@Component或@Bean注解,剩下的"工厂逻辑"全由框架处理。Spring 的BeanFactory本质上就是一个超级强大的简单工厂,配合配置文件或注解,实现了比传统工厂方法更灵活的扩展性。
五、避坑指南与最佳实践
拒绝过度设计 :
如果你的系统只有 3 种类型,且一年都不会变,强行套用"工厂方法"搞出 6 个类,那是过度设计 ,会被同事吐槽的。简单工厂 + 静态方法往往更香。
善用静态工厂方法 :
《Effective Java》强烈推荐用静态工厂方法代替构造器。例如:
java// 推荐写法 LocalDate date = LocalDate.now(); Optional<String> opt = Optional.of("value"); // 而不是 new LocalDate() 或 new Optional()这其实就是简单工厂的最佳实践。
结合策略模式 :
在实际业务中,工厂模式常与策略模式 搭配使用。例如文件上传场景,通过工厂根据配置(OSS、S3、本地)返回不同的上传策略对象,再结合 Spring 的
List<Service>自动注入,可以实现零if-else的优雅代码。
六、总结
工厂模式是面向对象设计的基石之一。
- 思想上 :推崇工厂方法,理解其"开闭原则"和"依赖倒置"的精髓,这是面试和架构设计的加分项。
- 行动上 :日常开发首选 Spring 依赖注入 ,其次是 简单工厂(静态方法)。
- 口诀 :"能注入就注入,不能注入写个静态方法,除非你在写框架。"
希望这篇文章能帮你理清工厂模式的脉络,在实际项目中做出最合适的技术选型。