Java设计模式之工厂模式
文章目录
- Java设计模式之工厂模式
-
- 前言
- 一、简单工厂模式
- 二、工厂方法模式
- 三、抽象工厂模式
- 四、三种工厂模式对比
- 五、Spring中的工厂模式应用
- 总结
- [✅ 亮点总结](#✅ 亮点总结)
- 适用场景
- 扩展方向
前言
工厂模式(Factory Pattern)是创建型设计模式中应用最广泛的模式家族,它封装了对象的创建逻辑,让调用方无需关心具体的实现细节。工厂模式分为三种演进形态:简单工厂 、工厂方法和抽象工厂。本文将通过一个"支付系统"的案例,逐步揭示它们的区别与适用场景。
学习建议:工厂模式的三种形态不是孤立存在的,而是一个随着需求复杂度增长而自然演进的过程。从一个小项目中"一个switch解决问题"的简单工厂,到产品种类增多后"为每个产品配一个工厂"的工厂方法,再到多产品族场景下"创建成套产品"的抽象工厂------这条演进脉络比记忆三种模式的类图更有价值。面试中常见的问题是让你比较这三种模式,并举例说明各自的使用场景。
一、简单工厂模式
简单工厂通过一个工厂类根据参数决定创建哪种产品实例:
java
// 产品接口
public interface Payment {
void pay(double amount);
}
// 具体产品
public class Alipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("支付宝支付: ¥" + amount);
}
}
public class WechatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("微信支付: ¥" + amount);
}
}
public class BankPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("银行卡支付: ¥" + amount);
}
}
// 简单工厂
public class PaymentFactory {
public static Payment createPayment(String type) {
switch (type) {
case "alipay": return new Alipay();
case "wechat": return new WechatPay();
case "bank": return new BankPay();
default:
throw new IllegalArgumentException("不支持的支付方式: " + type);
}
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
Payment payment = PaymentFactory.createPayment("alipay");
payment.pay(199.99);
}
}
优点 :将创建与使用分离,调用方只关心接口。
缺点 :新增支付方式需要修改工厂类的switch,违反开闭原则。适用于产品类型比较少且变化不频繁的场景。
简单工厂真的"简单"吗? 虽然叫"简单工厂",但在实际项目中很容易变复杂。比如你的switch分支从3个增长到20个,每个分支的创建逻辑从new Xxx()变成了一堆初始化代码------这时候简单工厂就变成了一个"上帝类",违背单一职责原则。另外,简单工厂通常用static方法,无法被子类重写,缺乏扩展点。如果预见到产品种类会频繁增加,应该从一开始就使用工厂方法模式。
二、工厂方法模式
定义一个创建对象的接口,让子类决定实例化哪个类:
java
// 抽象工厂
public abstract class PaymentFactory {
public abstract Payment createPayment();
// 通用业务逻辑可以放在工厂里
public void processPayment(double amount) {
Payment payment = createPayment();
payment.pay(amount);
System.out.println("支付记录已保存");
}
}
// 具体工厂
public class AlipayFactory extends PaymentFactory {
@Override
public Payment createPayment() {
return new Alipay();
}
}
public class WechatPayFactory extends PaymentFactory {
@Override
public Payment createPayment() {
return new WechatPay();
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
// 根据配置选择工厂
PaymentFactory factory = new AlipayFactory();
factory.processPayment(299.50);
}
}
优势:新增支付方式只需添加新的产品和工厂子类,无需修改已有代码,符合开闭原则。每个工厂只负责创建一种产品,职责单一。
不足:类的数量成倍增长。如果产品类型非常多,会出现大量的工厂子类。
如何取舍? 当产品只有3-5种时,简单工厂的switch足够清晰;当产品达到10+种且仍在增长时,工厂方法的优势开始显现。但有一个折中方案------结合反射或SPI机制:将产品类名配置化,在工厂中用Class.forName(className).newInstance()动态创建,这样既保持了单一工厂的简洁,又不需要改代码。Spring的BeanFactory本质上就是这样做的:通过配置(XML或注解)注册Bean定义,工厂按需创建实例。
三、抽象工厂模式
抽象工厂创建一组相关 或相互依赖的产品族:
java
// 产品族A:PC端UI组件
public interface Button {
void render();
}
public interface TextField {
void render();
}
// Windows实现
public class WinButton implements Button {
@Override
public void render() {
System.out.println("渲染Windows风格按钮");
}
}
public class WinTextField implements TextField {
@Override
public void render() {
System.out.println("渲染Windows风格文本框");
}
}
// Mac实现
public class MacButton implements Button {
@Override
public void render() {
System.out.println("渲染Mac风格按钮");
}
}
public class MacTextField implements TextField {
@Override
public void render() {
System.out.println("渲染Mac风格文本框");
}
}
// 抽象工厂:定义产品族接口
public interface UIFactory {
Button createButton();
TextField createTextField();
}
// 具体工厂:Windows产品族
public class WinUIFactory implements UIFactory {
@Override
public Button createButton() { return new WinButton(); }
@Override
public TextField createTextField() { return new WinTextField(); }
}
// 具体工厂:Mac产品族
public class MacUIFactory implements UIFactory {
@Override
public Button createButton() { return new MacButton(); }
@Override
public TextField createTextField() { return new MacTextField(); }
}
// 客户端
public class Application {
private Button button;
private TextField textField;
public Application(UIFactory factory) {
button = factory.createButton();
textField = factory.createTextField();
}
public void renderUI() {
button.render();
textField.render();
}
public static void main(String[] args) {
String os = System.getProperty("os.name");
UIFactory factory;
if (os.contains("Windows")) {
factory = new WinUIFactory();
} else {
factory = new MacUIFactory();
}
Application app = new Application(factory);
app.renderUI();
}
}
抽象工厂的核心在于产品族的概念。当系统中有一组产品需要一起使用时(如Windows UI套件、Mac UI套件),用抽象工厂确保使用同一产品族内的组件,避免混搭。
抽象工厂 vs 工厂方法的决策:最简单的判断标准是看"产品是否成组出现"。如果支付和退款总是成对使用(支付宝支付+支付宝退款、微信支付+微信退款),就可以用抽象工厂;如果只有一种产品类型,工厂方法就够用。强行在不合适的场景使用抽象工厂,会导致过度设计------代码量翻倍却没有带来实际价值。
四、三种工厂模式对比
| 维度 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 产品种类 | 1种(多个变体) | 1种(多个变体) | 多种(产品族) |
| 新增产品 | 修改工厂类 | 添加子类 | 添加新工厂 |
| 符合开闭原则 | 否 | 是 | 是 |
| 复杂度 | 低 | 中 | 高 |
| 典型场景 | JDBC DriverManager | Spring BeanFactory | MyBatis SqlSessionFactory |
五、Spring中的工厂模式应用
Spring的IoC容器本质上是一个巨大的工厂:
java
// 面试常问:BeanFactory和FactoryBean的区别
// BeanFactory是IoC容器的顶层接口,是工厂方法模式的体现
// FactoryBean是Spring提供的一个特殊Bean
// FactoryBean示例
@Component
public class HttpProxyFactoryBean implements FactoryBean<HttpProxy> {
@Override
public HttpProxy getObject() { return new HttpProxy("http://api.example.com"); }
@Override
public Class<?> getObjectType() { return HttpProxy.class; }
@Override
public boolean isSingleton() { return true; }
}
总结
工厂模式的核心思想是解耦对象的创建和使用。简单工厂是一个集中式工厂,适合产品类型固定的小项目;工厂方法将创建职责下放到子类,遵循开闭原则,适合需要灵活扩展的场景;抽象工厂处理产品族,适用于多套相关产品共存的系统。理解这三种模式的演进关系和应用场景,是设计好可扩展Java系统的关键。
终极理解:工厂模式解决的并不只是"怎么new对象"的问题------它解决的是"谁来负责创建对象"的职责分配问题。在面向对象设计中,"创建"本身就是一个值得封装的职责。把这层理解透了,你就能看懂Spring IoC为什么是工厂模式的集大成者:它不仅封装了创建,还管理了对象的整个生命周期(依赖注入、作用域、后置处理等)。
✅ 亮点总结
- 简单工厂 → 工厂方法 → 抽象工厂的渐进式演进,展示了产品种类增加时设计的自然变化
- 工厂方法遵循开闭原则------新增产品只需添加子工厂,无需修改已有代码
- 抽象工厂确保产品族的一致性------Windows/Mac UI套件不能被混搭使用
BeanFactoryvsFactoryBean的辨析------面试高频题,前者是IoC容器,后者是特殊工厂Bean- 三种模式的维度对比表(产品种类、扩展方式、开闭原则、复杂度、典型场景)一目了然
适用场景
- 多支付渠道接入------工厂方法为微信、支付宝、银联各自创建支付通道
- 多数据库支持------抽象工厂创建MySQL/Oracle/PostgreSQL各自的Connection和Command对象
- Spring Boot自动配置------
@Configuration+@Bean注解本质就是工厂方法的声明式写法
扩展方向
- Spring IoC容器源码深入 :从
BeanFactory→ApplicationContext的层次结构理解工厂模式的框架级应用 - 策略模式与工厂模式结合:工厂创建具体策略对象,策略执行不同行为,强强联合(推荐阅读下一篇:Java设计模式之代理模式)
- SPI(Service Provider Interface)机制 :Java SPI与Spring SPI
spring.factories的工厂扩展点