Java 抽象工厂模式从入门到实战(后端必备)
前言:抽象工厂模式是Java后端开发中最核心的创建型设计模式之一,也是工厂方法模式的进阶版。它解决了“多系列产品批量创建”的痛点,尤其适合复杂系统中“产品族”的统一管理,在框架底层(如Spring、MyBatis)、中间件开发中应用广泛。很多开发者只懂其表面用法,却分不清它与工厂方法模式的区别,面试时频频踩坑。本文从入门到实战,结合真实业务场景+可运行代码+面试高频考点,带你吃透抽象工厂模式,看完直接能用、能说、能面试。
一、为什么需要抽象工厂模式?(痛点直击)
做Java开发,你一定遇到过这样的场景,这些场景正是抽象工厂模式的用武之地,也是它与简单工厂、工厂方法模式的核心区别:
-
系统中存在“多系列产品”(产品族),比如电商系统的“支付产品族”(微信支付、支付宝支付),每个产品族下又有多个具体产品(支付、退款、对账),需要统一管理这些产品的创建;
-
希望屏蔽产品的创建细节,让调用方无需关心具体产品如何实例化,只需指定“产品族”,就能获取对应系列的所有产品;
-
系统需要具备良好的扩展性,后续新增产品族(如新增银联支付)时,无需修改原有代码,只需新增对应的工厂和产品,符合“开闭原则”;
-
避免简单工厂模式的“职责过重”和工厂方法模式“工厂类泛滥”的问题,实现产品创建的规范化、模块化。
举个真实场景:电商系统的支付模块,微信支付和支付宝支付是两个独立的产品族,每个产品族都包含“支付”“退款”“对账”三个具体产品。如果用工厂方法模式,需要为每个具体产品(微信支付、支付宝支付、微信退款、支付宝退款)创建一个工厂类,会导致工厂类数量暴增;如果用简单工厂,会让一个工厂类承担所有产品的创建逻辑,难以维护。而抽象工厂模式,只需一个抽象工厂接口,对应每个产品族一个具体工厂,就能统一管理该产品族下的所有产品创建,完美解决上述问题。
核心结论:当需要创建“多系列、多产品”的产品族,且希望统一管理、灵活扩展时,就用抽象工厂模式。
二、抽象工厂模式核心概念(极简理解)
抽象工厂模式的核心思想:“抽象工厂定义产品族的创建接口,具体工厂实现对应产品族的所有产品创建,调用方通过抽象工厂获取产品,无需关心具体实现”。
与工厂方法模式不同,抽象工厂模式关注“产品族”,而工厂方法模式关注“单一产品”。简单说,工厂方法是“一个工厂造一个产品”,抽象工厂是“一个工厂造一个系列的产品”。
抽象工厂模式有4个核心角色(理论+实战结合,重点记实战用法):
-
AbstractFactory(抽象工厂):核心接口,定义一个产品族中所有产品的创建方法(比如创建支付、创建退款、创建对账),所有具体工厂都必须实现这个接口;
-
ConcreteFactory(具体工厂):实现抽象工厂接口,负责创建某一个具体产品族的所有产品(比如微信支付工厂,负责创建微信支付、微信退款、微信对账);
-
AbstractProduct(抽象产品):定义某一类产品的统一接口(比如支付接口、退款接口),所有具体产品都必须实现这个接口;
-
ConcreteProduct(具体产品):实现抽象产品接口,是抽象工厂创建的具体实例(比如微信支付、支付宝支付、微信退款)。
重点:抽象工厂模式的关键是“产品族”和“产品等级”。产品族是同一品牌/系列的产品(如微信支付系列),产品等级是同一类型的产品(如所有支付产品)。抽象工厂定义产品等级的创建接口,具体工厂实现产品族的创建逻辑。
三、实战一:手写抽象工厂模式(基础必备)
以电商系统“支付产品族”为场景,手动实现抽象工厂模式,理解其底层逻辑。场景说明:存在微信支付、支付宝支付两个产品族,每个产品族包含“支付”“退款”两个具体产品(产品等级),用抽象工厂统一管理。
步骤1:定义抽象产品(统一产品接口)
先定义两个产品等级的抽象接口:支付接口、退款接口,规范所有具体产品的行为。
/**
* 抽象产品1:支付接口(定义支付产品的统一行为)
*/
public interface Payment {
// 支付方法
void pay(double amount);
// 查询支付状态
String queryPayStatus(String orderNo);
}
/**
* 抽象产品2:退款接口(定义退款产品的统一行为)
*/
public interface Refund {
// 退款方法
void refund(double amount, String orderNo);
// 查询退款状态
String queryRefundStatus(String refundNo);
}
步骤2:定义具体产品(实现抽象产品接口)
实现两个产品族(微信、支付宝)的具体产品,每个产品族对应两个具体产品。
// ---------------------- 微信产品族 ----------------------
/**
* 具体产品:微信支付(实现支付接口)
*/
public class WeChatPayment implements Payment {
@Override
public void pay(double amount) {
System.out.println("微信支付:支付金额" + amount + "元,已完成扣款");
}
@Override
public String queryPayStatus(String orderNo) {
// 模拟查询支付状态(实际中调用微信支付接口)
return "微信支付订单[" + orderNo + "]:支付成功";
}
}
/**
* 具体产品:微信退款(实现退款接口)
*/
public class WeChatRefund implements Refund {
@Override
public void refund(double amount, String orderNo) {
System.out.println("微信退款:订单[" + orderNo + "],退款金额" + amount + "元,已发起退款");
}
@Override
public String queryRefundStatus(String refundNo) {
return "微信退款订单[" + refundNo + "]:退款成功,已到账";
}
}
// ---------------------- 支付宝产品族 ----------------------
/**
* 具体产品:支付宝支付(实现支付接口)
*/
public class AlipayPayment implements Payment {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:支付金额" + amount + "元,已完成扣款");
}
@Override
public String queryPayStatus(String orderNo) {
return "支付宝支付订单[" + orderNo + "]:支付成功";
}
}
/**
* 具体产品:支付宝退款(实现退款接口)
*/
public class AlipayRefund implements Refund {
@Override
public void refund(double amount, String orderNo) {
System.out.println("支付宝退款:订单[" + orderNo + "],退款金额" + amount + "元,已发起退款");
}
@Override
public String queryRefundStatus(String refundNo) {
return "支付宝退款订单[" + refundNo + "]:退款成功,已到账";
}
}
步骤3:定义抽象工厂(统一产品族创建接口)
抽象工厂接口定义该产品族下所有产品的创建方法,对应两个产品等级(支付、退款),各一个创建方法。
/**
* 抽象工厂:支付产品族工厂接口
* 定义一个产品族(支付、退款)的所有产品创建方法
*/
public interface PaymentFactory {
// 创建支付产品
Payment createPayment();
// 创建退款产品
Refund createRefund();
}
步骤4:定义具体工厂(实现产品族创建逻辑)
每个产品族对应一个具体工厂,实现抽象工厂的所有方法,创建该产品族的具体产品。
/**
* 具体工厂1:微信支付工厂(创建微信产品族的所有产品)
*/
public class WeChatPaymentFactory implements PaymentFactory {
@Override
public Payment createPayment() {
// 创建微信支付产品
return new WeChatPayment();
}
@Override
public Refund createRefund() {
// 创建微信退款产品
return new WeChatRefund();
}
}
/**
* 具体工厂2:支付宝支付工厂(创建支付宝产品族的所有产品)
*/
public class AlipayPaymentFactory implements PaymentFactory {
@Override
public Payment createPayment() {
// 创建支付宝支付产品
return new AlipayPayment();
}
@Override
public Refund createRefund() {
// 创建支付宝退款产品
return new AlipayRefund();
}
}
步骤5:客户端使用(调用抽象工厂获取产品)
客户端无需关心具体产品的创建细节,只需获取具体工厂,再通过工厂获取对应产品,实现“按需获取产品族”。
/**
* 客户端:电商支付模块调用示例
* 核心:通过抽象工厂获取产品,无需关心具体实现
*/
public class PaymentClient {
public static void main(String[] args) {
// 1. 使用微信支付产品族
PaymentFactory weChatFactory = new WeChatPaymentFactory();
Payment weChatPay = weChatFactory.createPayment();
Refund weChatRefund = weChatFactory.createRefund();
// 调用微信支付和退款
weChatPay.pay(199.9);
System.out.println(weChatPay.queryPayStatus("WECHAT20260415001"));
weChatRefund.refund(199.9, "WECHAT20260415001");
System.out.println(weChatRefund.queryRefundStatus("WECHAT_REFUND20260415001"));
System.out.println("----------------------");
// 2. 使用支付宝支付产品族(切换产品族只需更换具体工厂)
PaymentFactory alipayFactory = new AlipayPaymentFactory();
Payment alipay = alipayFactory.createPayment();
Refund alipayRefund = alipayFactory.createRefund();
// 调用支付宝支付和退款
alipay.pay(299.9);
System.out.println(alipay.queryPayStatus("ALIPAY20260415001"));
alipayRefund.refund(299.9, "ALIPAY20260415001");
System.out.println(alipayRefund.queryRefundStatus("ALIPAY_REFUND20260415001"));
}
}
运行结果(重点观察)
微信支付:支付金额199.9元,已完成扣款
微信支付订单[WECHAT20260415001]:支付成功
微信退款:订单[WECHAT20260415001],退款金额199.9元,已发起退款
微信退款订单[WECHAT_REFUND20260415001]:退款成功,已到账
----------------------
支付宝支付:支付金额299.9元,已完成扣款
支付宝支付订单[ALIPAY20260415001]:支付成功
支付宝退款:订单[ALIPAY20260415001],退款金额299.9元,已发起退款
支付宝退款订单[ALIPAY_REFUND20260415001]:退款成功,已到账
手写抽象工厂核心要点
-
抽象工厂定义“产品族”的创建接口,每个方法对应一个“产品等级”,确保产品族的完整性;
-
具体工厂与产品族一一对应,一个具体工厂负责创建该产品族下的所有产品;
-
客户端只依赖抽象工厂和抽象产品,不依赖具体工厂和具体产品,实现“依赖倒置原则”;
-
切换产品族只需更换具体工厂实例,无需修改客户端代码,扩展性极强。
四、实战二:抽象工厂模式进阶(结合Spring,实战常用)
实际开发中,我们不会手动new具体工厂,而是结合Spring框架的依赖注入(DI),将具体工厂交给Spring管理,客户端通过Spring获取工厂实例,进一步降低耦合,这也是企业开发中的主流用法。
步骤1:将具体工厂交给Spring管理(两种方式)
方式一:注解方式(推荐,简洁高效)
import org.springframework.stereotype.Component;
// 微信支付工厂:交给Spring管理,指定bean名称
@Component("weChatPaymentFactory")
public class WeChatPaymentFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new WeChatPayment();
}
@Override
public Refund createRefund() {
return new WeChatRefund();
}
}
// 支付宝支付工厂:交给Spring管理
@Component("alipayPaymentFactory")
public class AlipayPaymentFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new AlipayPayment();
}
@Override
public Refund createRefund() {
return new AlipayRefund();
}
}
方式二:XML配置方式(兼容旧项目)
<!-- Spring配置文件:注册具体工厂bean -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 微信支付工厂bean -->
<bean id="weChatPaymentFactory" class="com.example.factory.WeChatPaymentFactory"/>
<!-- 支付宝支付工厂bean -->
<bean id="alipayPaymentFactory" class="com.example.factory.AlipayPaymentFactory"/>
</beans>
步骤2:客户端通过Spring获取工厂和产品
通过Spring的ApplicationContext获取具体工厂,再调用工厂方法创建产品,无需手动new对象。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 结合Spring的客户端调用(企业开发实战版)
*/
public class SpringPaymentClient {
public static void main(String[] args) {
// 1. 加载Spring配置,获取容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
// 2. 获取微信支付工厂(通过bean名称)
PaymentFactory weChatFactory = context.getBean("weChatPaymentFactory", PaymentFactory.class);
Payment weChatPay = weChatFactory.createPayment();
weChatPay.pay(399.9);
// 3. 获取支付宝支付工厂
PaymentFactory alipayFactory = context.getBean("alipayPaymentFactory", PaymentFactory.class);
Refund alipayRefund = alipayFactory.createRefund();
alipayRefund.refund(399.9, "ALIPAY20260415002");
}
}
进阶要点(企业开发避坑)
-
实际开发中,具体产品(如WeChatPayment)也可交给Spring管理,在具体工厂中通过@Autowired注入,而非手动new;
-
可通过Spring的@Profile注解,实现不同环境(开发、测试、生产)切换不同的产品族(如开发环境用微信支付,生产环境用支付宝支付);
-
抽象工厂模式常与单例模式结合,确保具体工厂只有一个实例,节省资源(Spring的bean默认是单例,无需额外实现)。
五、实战三:框架中的抽象工厂模式(面试必说)
抽象工厂模式在Java主流框架中应用广泛,面试时能说出1-2个,会显得你理解更深刻,不是只懂理论。
1. Spring 框架:BeanFactory(核心应用)
Spring的BeanFactory是抽象工厂模式的经典实现,也是Spring容器的核心接口。
-
AbstractFactory(抽象工厂):BeanFactory接口,定义了获取Bean(产品)的方法(getBean());
-
ConcreteFactory(具体工厂):XmlBeanFactory、AnnotationConfigApplicationContext等,实现BeanFactory接口,负责创建和管理Bean;
-
AbstractProduct(抽象产品):Object(所有Bean的父类);
-
ConcreteProduct(具体产品):Spring管理的所有Bean(如Service、Dao、Controller)。
原理:我们通过Spring容器(具体工厂)获取Bean(具体产品),无需关心Bean的创建细节(如初始化、依赖注入),完美契合抽象工厂模式的思想。
2. MyBatis 框架:SqlSessionFactory(核心应用)
MyBatis中的SqlSessionFactory也是抽象工厂模式的实现,负责创建SqlSession(产品)。
// 1. 抽象工厂:SqlSessionFactory(定义创建SqlSession的方法)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 具体工厂:DefaultSqlSessionFactory(SqlSessionFactory的实现类)
// 3. 具体产品:SqlSession(通过工厂创建,用于执行SQL)
SqlSession session = factory.openSession();
核心:SqlSessionFactory作为抽象工厂,定义了创建SqlSession的接口,DefaultSqlSessionFactory作为具体工厂,实现创建逻辑,SqlSession作为具体产品,提供SQL执行能力。
3. 实际项目场景:日志框架适配
项目中需要适配不同的日志框架(Log4j、SLF4J、Logback),每个日志框架对应一个产品族,包含“日志记录”“日志级别设置”“日志输出”三个产品等级,用抽象工厂模式统一管理,后续切换日志框架时无需修改业务代码。
六、抽象工厂模式面试高频考点(必背)
1. 抽象工厂模式的核心优势?
-
解耦:屏蔽产品创建细节,客户端只依赖抽象接口,降低代码耦合度;
-
规范:统一管理产品族的创建,确保产品族的完整性和一致性;
-
扩展灵活:新增产品族时,只需新增具体工厂和具体产品,无需修改原有代码,符合“开闭原则”;
-
避免冗余:解决工厂方法模式“工厂类泛滥”的问题,一个具体工厂管理一个产品族。
2. 抽象工厂模式和工厂方法模式的区别?(高频中的高频)
核心区别:关注焦点不同,工厂方法关注“单一产品”,抽象工厂关注“产品族”。
| 对比维度 | 抽象工厂模式 | 工厂方法模式 |
|---|---|---|
| 关注焦点 | 产品族(多系列、多产品) | 单一产品(单个产品等级) |
| 工厂职责 | 创建一个产品族的所有产品 | 创建一个具体产品 |
| 工厂数量 | 与产品族数量一致(数量少) | 与具体产品数量一致(数量多) |
| 扩展性 | 新增产品族容易,新增产品等级困难 | 新增产品等级容易,新增产品族困难 |
| 适用场景 | 多系列产品、需要统一管理的场景 | 单一产品、需要灵活扩展产品的场景 |
3. 抽象工厂模式的缺点?(避坑重点)
-
扩展性局限:新增产品等级(如在支付产品族中新增“对账”产品)时,需要修改抽象工厂接口和所有具体工厂,违反“开闭原则”;
-
复杂度高:当产品族和产品等级较多时,会产生大量的工厂类和产品类,增加系统复杂度;
-
灵活性不足:产品族的产品数量固定,无法动态增加或减少产品。
4. 什么时候用抽象工厂模式?
-
系统中存在多个产品族,且每个产品族包含多个具体产品(如支付、日志、数据库适配);
-
希望屏蔽产品创建细节,客户端无需关心具体产品的实现,只需获取产品族;
-
系统需要灵活切换产品族,且新增产品族的频率高于新增产品等级;
-
需要统一管理产品族的创建,确保产品之间的兼容性(如微信支付和微信退款必须配套使用)。
5. 抽象工厂模式的实际应用场景?(面试加分)
-
框架底层:Spring BeanFactory、MyBatis SqlSessionFactory;
-
多数据库适配:MySQL、Oracle、SQL Server对应不同的产品族,包含连接、查询、更新等产品;
-
支付系统:微信、支付宝、银联等支付产品族;
-
日志框架:Log4j、SLF4J、Logback等日志产品族;
-
UI组件库:不同主题(浅色、深色)的UI组件族(按钮、输入框、下拉框)。
七、总结(实战+面试双达标)
对于Java后端开发者来说,抽象工厂模式不是“理论性设计模式”,而是企业开发中规范产品创建、降低耦合的核心工具,也是面试中高频考察的重点(尤其是与工厂方法模式的区别)。
-
基础:理解抽象工厂模式的4个核心角色,掌握“产品族”和“产品等级”的区别;
-
实战:能手动实现抽象工厂模式,结合Spring框架实现依赖注入,适配真实业务场景;
-
面试:能清晰区分抽象工厂与工厂方法模式,说出框架中的应用场景,掌握其优缺点和适用场景;
-
避坑:记住“新增产品族容易、新增产品等级困难”,根据项目场景选择是否使用抽象工厂模式。
记住一句话:单一产品用工厂方法,多系列产品用抽象工厂;核心是“统一管理产品族,屏蔽创建细节,灵活扩展”。掌握它,能让你的代码更规范、更易维护,面试时也能轻松应对设计模式相关提问。
补充:常见问题解决
-
问题1:新增产品等级时,如何避免修改抽象工厂接口?
解决:可结合工厂方法模式,在抽象工厂中定义一个创建产品的抽象方法,具体工厂通过工厂方法创建具体产品,减少接口修改(本质是“抽象工厂+工厂方法”结合)。 -
问题2:如何简化抽象工厂模式的复杂度?
解决:结合Spring依赖注入,将工厂和产品交给Spring管理,避免手动创建大量工厂类;同时控制产品族和产品等级的数量,避免过度设计。 -
问题3:抽象工厂模式和单例模式如何结合?
解决:将具体工厂交给Spring管理(Spring bean默认单例),或在具体工厂中实现单例模式(如饿汉式、懒汉式),确保工厂实例唯一。
文章介绍了从简单工厂模式开始,如何通过工厂方法和抽象工厂模式提高代码的扩展性和结构。简单工厂适用于简单场景,但不易扩展。工厂方法允许创建对象的工厂可被子类化,而抽象工厂则能创建一族相关或相互依赖的对象,提供了更灵活的解决方案。
2302

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



