👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
一、入门
什么是抽象工厂模式?
抽象工厂模式是一种创建型设计模式,它提供了一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。
简单来说,抽象工厂模式是工厂方法模式的升级版,它能够创建一组相关的对象,而工厂方法模式只能创建一种对象。
为什么要抽象工厂模式?
假设你要购买一套家具(沙发、茶几、电视柜),并且希望这些家具的风格保持一致(比如现代风格或古典风格)。如果没有抽象工厂模式,下面是没有抽象工厂的实现:
// 现代风格沙发
class ModernSofa {
public void lieOn() {
System.out.println("躺在现代风格沙发上");
}
}
// 古典风格沙发
class ClassicSofa {
public void lieOn() {
System.out.println("躺在古典风格沙发上");
}
}
// 现代风格茶几
class ModernCoffeeTable {
public void putOn() {
System.out.println("在现代风格茶几上放东西");
}
}
// 古典风格茶几
class ClassicCoffeeTable {
public void putOn() {
System.out.println("在古典风格茶几上放东西");
}
}
// 客户端代码
public class FurnitureShop {
public static void main(String[] args) {
// 选择现代风格
ModernSofa modernSofa = new ModernSofa();
ModernCoffeeTable modernCoffeeTable = new ModernCoffeeTable();
modernSofa.lieOn();
modernCoffeeTable.putOn();
// 选择古典风格
ClassicSofa classicSofa = new ClassicSofa();
ClassicCoffeeTable classicCoffeeTable = new ClassicCoffeeTable();
classicSofa.lieOn();
classicCoffeeTable.putOn();
}
}
存在问题:
- 风格一致性难以保证:
- 如果客户端代码不小心混用了不同风格的对象(比如现代风格沙发 + 古典风格茶几),会导致风格不一致。
ModernSofa sofa = new ModernSofa();
ClassicCoffeeTable table = new ClassicCoffeeTable(); // 风格不一致!
- 代码重复:
- 每次创建一组家具时,都需要重复编写类似的代码。
ModernSofa sofa = new ModernSofa();
ModernCoffeeTable table = new ModernCoffeeTable(); - 如果家具种类增多(比如增加电视柜、餐桌),代码会变得更加冗长。
- 每次创建一组家具时,都需要重复编写类似的代码。
- 难以扩展:
- 如果新增一种风格(比如北欧风格),需要修改客户端代码,手动创建新的对象:
NordicSofa sofa = new NordicSofa();
NordicCoffeeTable table = new NordicCoffeeTable(); - 这会违反开闭原则(对扩展开放,对修改关闭)。
- 如果新增一种风格(比如北欧风格),需要修改客户端代码,手动创建新的对象:
- 客户端代码与具体类耦合:
- 客户端代码直接依赖具体的家具类(如
ModernSofa
、ClassicCoffeeTable
),如果具体类发生变化(比如类名修改),客户端代码也需要跟着修改。
- 客户端代码直接依赖具体的家具类(如
如何实现抽象工厂模式?
- 抽象工厂(Abstract Factory):声明一组创建产品对象的方法,每个方法对应一种产品。
- 具体工厂(Concrete Factory):实现抽象工厂接口,负责创建具体的产品对象。
- 抽象产品(Abstract Product):声明产品的接口。
- 具体产品(Concrete Product):实现抽象产品接口,定义具体产品的行为。
【案例】家具城 - 改
抽象产品(Abstract Product):沙发、茶几。
// 沙发
interface Sofa {
void lieOn();
}
// 茶几
interface CoffeeTable {
void putOn();
}
具体产品(Concrete Product): 现代沙发、古典风格沙发、现代风格茶几、古典风格茶几。
// 具体产品:现代风格沙发
class ModernSofa implements Sofa {
public void lieOn() {
System.out.println("躺在现代风格沙发上");
}
}
// 具体产品:古典风格沙发
class ClassicSofa implements Sofa {
public void lieOn() {
System.out.println("躺在古典风格沙发上");
}
}
// 具体产品:现代风格茶几
class ModernCoffeeTable implements CoffeeTable {
public void putOn() {
System.out.println("在现代风格茶几上放东西");
}
}
// 具体产品:古典风格茶几
class ClassicCoffeeTable implements CoffeeTable {
public void putOn() {
System.out.println("在古典风格茶几上放东西");
}
}
抽象工厂(Abstract Factory): 家具工厂
interface FurnitureFactory {
Sofa createSofa();
CoffeeTable createCoffeeTable();
}
具体工厂(Concrete Factory):现代风格家具工厂、古典风格家具工厂。
// 具体工厂:现代风格家具工厂
class ModernFurnitureFactory implements FurnitureFactory {
public Sofa createSofa() {
return new ModernSofa();
}
public CoffeeTable createCoffeeTable() {
return new ModernCoffeeTable();
}
}
// 具体工厂:古典风格家具工厂
class ClassicFurnitureFactory implements FurnitureFactory {
public Sofa createSofa() {
return new ClassicSofa();
}
public CoffeeTable createCoffeeTable() {
return new ClassicCoffeeTable();
}
}
客户端
public class FurnitureShop {
private Sofa sofa;
private CoffeeTable coffeeTable;
public FurnitureShop(FurnitureFactory factory) {
sofa = factory.createSofa();
coffeeTable = factory.createCoffeeTable();
}
public void useFurniture() {
sofa.lieOn();
coffeeTable.putOn();
}
public static void main(String[] args) {
// 选择现代风格家具工厂
FurnitureFactory modernFactory = new ModernFurnitureFactory();
FurnitureShop shop = new FurnitureShop(modernFactory);
shop.useFurniture();
// 选择古典风格家具工厂
FurnitureFactory classicFactory = new ClassicFurnitureFactory();
shop = new FurnitureShop(classicFactory);
shop.useFurniture();
}
}
抽象工厂模式和工厂方法的区别
场景 | 工厂方法模式 | 抽象工厂模式 |
---|---|---|
创建目标 | 单个产品(如沙发) | 一组相关产品(如沙发、茶几、电视柜) |
风格一致性 | 无法保证 | 保证一组对象的风格一致 |
类的数量 | 每个产品需要一个工厂接口和具体工厂类 | 每个产品家族需要一个具体工厂类 |
客户端代码复杂度 | 需要分别调用多个工厂 | 只需调用一个工厂即可创建一组对象 |
适用场景 | 单一产品的创建 | 一组相关或依赖对象的创建 |
二、抽象工厂模式在框架源码中的运用
JDBC 中的 java.sql.Connection
JDBC(Java Database Connectivity
)是Java中用于操作数据库的标准API,其中的 Connection
和 Statement
等接口也体现了抽象工厂模式。
public class JDBCExample {
public static void main(String[] args) throws Exception {
// 获取数据库连接(抽象工厂)
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
// 创建Statement(具体产品)
Statement statement = connection.createStatement();
// 执行查询
ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
// 处理结果
while (resultSet.next()) {
System.out.println(resultSet.getString("username"));
}
}
}
抽象工厂模式的应用
- 抽象工厂:
Connection
是一个抽象工厂,定义了创建Statement
、PreparedStatement
等方法。 - 具体工厂:不同的数据库驱动(如MySQL、Oracle)提供了具体的
Connection
实现。 - 产品家族:
Statement
、PreparedStatement
、ResultSet
是相关的产品,用于执行SQL语句和处理结果。
Spring 框架中的 BeanFactory
Spring 框架的核心容器 BeanFactory
也是一个典型的抽象工厂模式应用。
public class SpringExample {
public static void main(String[] args) {
// 加载Spring配置文件
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
// 获取Bean(具体产品)
MyService myService = (MyService) factory.getBean("myService");
myService.execute();
}
}
抽象工厂模式的应用
- 抽象工厂:
BeanFactory
是一个抽象工厂,定义了获取Bean的方法(如getBean
)。 - 具体工厂:
XmlBeanFactory
是一个具体工厂,负责从XML配置文件中创建和管理Bean。 - 产品家族:Spring容器管理的Bean(如 MyService)是相关的产品,它们可能相互依赖或协作。
三、总结
抽象工厂模式的优点
- 保证产品家族的一致性:
- 抽象工厂模式确保创建的对象是同一家族的,比如现代风格的沙发、茶几、电视柜,避免了风格不一致的问题。
- 解耦客户端代码:
- 客户端代码只依赖于抽象工厂和抽象产品接口,不依赖于具体的实现类,符合依赖倒置原则。
- 符合开闭原则:
- 新增产品家族(如新增北欧风格家具)时,只需增加新的具体工厂类,不需要修改现有代码。
- 简化客户端代码:
- 客户端不需要关心具体对象的创建细节,只需要调用工厂方法即可。
- 提高代码可维护性:
- 将对象的创建逻辑集中到工厂中,便于统一管理和维护。
抽象工厂模式的缺点
- 类的数量增加:
- 每增加一个产品家族,就需要增加一组具体工厂类和具体产品类,可能导致类的数量爆炸。
- 扩展产品类型困难:
- 如果需要在现有产品家族中新增产品类型(比如在家具家族中新增“餐桌”),需要修改抽象工厂接口及其所有具体工厂类,违反了开闭原则。
- 复杂性增加:
- 对于简单的对象创建场景,使用抽象工厂模式可能会引入不必要的复杂性。
- 理解难度较高:
- 抽象工厂模式涉及多个抽象层(抽象工厂、抽象产品、具体工厂、具体产品),初学者可能难以理解。
抽象工厂模式的适用场景
抽象工厂模式适用于以下场景:
- 需要创建一组相关或依赖的对象:
- 比如跨平台的UI组件(Windows按钮、Mac按钮)、家具家族(现代风格沙发、茶几)、数据库访问对象(
Connection
、Statement
)等。
- 比如跨平台的UI组件(Windows按钮、Mac按钮)、家具家族(现代风格沙发、茶几)、数据库访问对象(
- 需要保证产品家族的一致性:
- 比如确保所有UI组件都是同一风格,或者所有家具都是同一主题。
- 系统需要独立于产品的创建、组合和表示:
- 比如框架或库的设计,希望将对象的创建逻辑与使用逻辑分离。
- 需要支持多种产品家族:
- 比如支持多种数据库(MySQL、Oracle)、多种操作系统(Windows、Mac)、多种主题(现代、古典)等。
- 希望通过配置或依赖注入动态切换产品家族:
- 比如Spring框架中通过配置文件切换具体的
Bean
实现。
- 比如Spring框架中通过配置文件切换具体的