概述
抽象工厂模式(Abstract Factory Pattern) 是GoF(Gang of Four)所归纳的创建型设计模式之一,其标准定义为:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。该模式的核心意图在于封装产品族的创建过程,确保客户端在运行时能够透明地切换整个产品族,同时严格保证产品族内各对象之间的兼容性与一致性。
在软件工程实践中,抽象工厂模式解决的最根本问题是跨平台对象创建与产品族约束管理。试想一个跨操作系统的UI框架,按钮(Button)与文本框(TextField)必须属于同一视觉风格族——Windows风格或Mac风格。若客户端直接通过new实例化具体组件,极易出现Windows按钮搭配Mac文本框的混乱场景,破坏用户体验与系统稳定性。抽象工厂通过将产品族创建收敛于单一工厂接口,从根本上杜绝了产品族混用的风险。
本文将带领读者完成一次从基础到专家的技术深潜之旅。我们首先回顾工厂方法模式在面对产品族时的局限性,进而引出抽象工厂的完整结构与实现;随后深入JDK、Spring、MyBatis等主流框架的源码层面,剖析抽象工厂模式的真实落地细节;紧接着专设章节探讨分布式环境下抽象工厂的演进应用,涵盖微服务客户端管理、多租户数据源路由、多云存储适配等前沿场景;最后通过对比辨析与面试题精解,帮助读者构建完整且坚固的知识体系。全文约一万两千字,请准备好开启这场思维盛宴。
一、模式定义与结构
1.1 GoF标准定义
Abstract Factory:Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
该定义强调两点:
- 系列对象:工厂负责创建的不是单一产品,而是一组具有内在关联的产品(即产品族)。
- 接口隔离:客户端仅依赖抽象接口,具体产品的实例化被延迟到具体工厂子类中完成。
1.2 Mermaid UML类图(flowchart语法)
flowchart TB
subgraph 抽象层
AF[AbstractFactory<br/>抽象工厂接口]
AP1[AbstractProductA<br/>抽象产品A]
AP2[AbstractProductB<br/>抽象产品B]
end
subgraph 具体工厂层
CF1[ConcreteFactory1<br/>具体工厂1]
CF2[ConcreteFactory2<br/>具体工厂2]
end
subgraph 具体产品层
P1A1[ProductA1<br/>产品A1]
P1B1[ProductB1<br/>产品B1]
P2A2[ProductA2<br/>产品A2]
P2B2[ProductB2<br/>产品B2]
end
AF -->|定义创建方法| AP1
AF -->|定义创建方法| AP2
CF1 -->|实现| AF
CF2 -->|实现| AF
CF1 -->|创建| P1A1
CF1 -->|创建| P1B1
CF2 -->|创建| P2A2
CF2 -->|创建| P2B2
P1A1 -->|实现| AP1
P1B1 -->|实现| AP2
P2A2 -->|实现| AP1
P2B2 -->|实现| AP2
classDef abstract fill:#f9f,stroke:#333,stroke-width:2px;
classDef concrete fill:#bbf,stroke:#333,stroke-width:2px;
class AF,AP1,AP2 abstract;
class CF1,CF2,P1A1,P1B1,P2A2,P2B2 concrete;
图表解读(不少于200字):
上图为抽象工厂模式的标准化UML类图,严格遵循flowchart语法绘制。图中清晰划分出三个层次:抽象层、具体工厂层、具体产品层。抽象工厂接口AbstractFactory定义了创建两个抽象产品的方法createProductA()和createProductB(),这两个方法返回的类型分别是抽象产品接口AbstractProductA和AbstractProductB。具体工厂ConcreteFactory1和ConcreteFactory2分别实现了抽象工厂接口,每个具体工厂负责创建属于同一个产品族的所有具体产品。例如,ConcreteFactory1创建ProductA1与ProductB1,二者共同构成产品族1;ConcreteFactory2创建ProductA2与ProductB2,构成产品族2。
此处需特别区分两个极易混淆的概念:产品等级结构与产品族。产品等级结构指的是同一类型产品的不同实现所形成的继承体系,例如AbstractProductA与ProductA1、ProductA2之间构成一个产品等级结构。而产品族是指由同一个具体工厂所生产的一组位于不同产品等级结构中的产品集合,例如ProductA1与ProductB1同属产品族1。抽象工厂模式的精髓就在于它将产品族的创建逻辑封装在具体工厂中,从而保证了跨产品等级的对象在运行时能够协同工作而不发生错配。
1.3 各角色职责与协作关系
| 角色 | 职责 | 协作关系 |
|---|---|---|
| AbstractFactory | 声明创建抽象产品对象的操作接口 | 被客户端调用,具体创建逻辑由子类实现 |
| ConcreteFactory | 实现创建具体产品对象的操作 | 实现抽象工厂接口,负责实例化同一产品族内的所有具体产品 |
| AbstractProduct | 为一类产品对象声明接口 | 作为抽象工厂创建方法的返回类型,客户端仅依赖此接口 |
| ConcreteProduct | 实现抽象产品接口,定义具体产品对象 | 由具体工厂实例化,并与同族的其他具体产品协同工作 |
协作流程:
- 系统启动时,客户端根据配置或上下文选择并实例化一个具体工厂对象(通常为单例)。
- 客户端调用具体工厂的各个创建方法,获取一组抽象产品实例。
- 客户端仅面向抽象产品接口编程,无需关心具体实现类。
- 当需要切换产品族时,仅需更换具体工厂实例,客户端代码零修改。
二、代码演进与实现
本节将通过一个跨平台UI组件库的案例,从最原始的new方式出发,逐步演进至抽象工厂模式,并展示其变体应用。所有代码均为可独立运行的完整Demo。
2.1 不使用模式的原始代码及其风险
假设我们需要开发一个支持Windows和Mac两种风格的对话框,其中包含按钮(Button)和文本框(TextField)。若直接使用具体类实例化,代码可能如下:
// 具体产品类:Windows风格组件
class WindowsButton {
public void click() { System.out.println("Windows按钮点击"); }
}
class WindowsTextField {
public void input() { System.out.println("Windows文本框输入"); }
}
// 具体产品类:Mac风格组件
class MacButton {
public void click() { System.out.println("Mac按钮点击"); }
}
class MacTextField {
public void input() { System.out.println("Mac文本框输入"); }
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 假设当前需要Windows风格
WindowsButton btn = new WindowsButton();
WindowsTextField text = new WindowsTextField();
btn.click();
text.input();
// 风险演示:开发者可能因疏忽混用不同产品族的组件
WindowsButton btn2 = new WindowsButton();
MacTextField text2 = new MacTextField(); // 风格不一致!
btn2.click();
text2.input(); // 运行时会输出不协调的界面行为
}
}
问题分析:
- 产品族一致性无法保证:如注释所述,开发者可能无意中将Windows按钮与Mac文本框组合,导致界面风格混乱。
- 客户端与具体产品类强耦合:若需要切换整个UI风格,必须修改客户端中所有
new语句的类名,维护成本极高。 - 扩展困难:新增一套Linux风格时,所有客户端代码均需修改。
2.2 工厂方法模式的局限
为了解耦对象创建过程,我们首先想到的是工厂方法模式。然而,当存在多个相互依赖的产品时,工厂方法模式会暴露出严重不足。
// 抽象产品接口
interface Button { void click(); }
interface TextField { void input(); }
// 具体产品实现
class WindowsButtonImpl implements Button {
public void click() { System.out.println("Windows按钮点击"); }
}
class MacButtonImpl implements Button {
public void click() { System.out.println("Mac按钮点击"); }
}
class WindowsTextFieldImpl implements TextField {
public void input() { System.out.println("Windows文本框输入"); }
}
class MacTextFieldImpl implements TextField {
public void input() { System.out.println("Mac文本框输入"); }
}
// 工厂方法:为每个产品定义一个创建接口
interface ButtonFactory { Button createButton(); }
interface TextFieldFactory { TextField createTextField(); }
class WindowsButtonFactory implements ButtonFactory {
public Button createButton() { return new WindowsButtonImpl(); }
}
class MacButtonFactory implements ButtonFactory {
public Button createButton() { return new MacButtonImpl(); }
}
class WindowsTextFieldFactory implements TextFieldFactory {
public TextField createTextField() { return new WindowsTextFieldImpl(); }
}
class MacTextFieldFactory implements TextFieldFactory {
public TextField createTextField() { return new MacTextFieldImpl(); }
}
// 客户端代码
public class FactoryMethodClient {
public static void main(String[] args) {
// 要构建一个Windows风格的对话框,需要分别获取两个工厂
ButtonFactory btnFactory = new WindowsButtonFactory();
TextFieldFactory textFactory = new WindowsTextFieldFactory();
Button btn = btnFactory.createButton();
TextField text = textFactory.createTextField();
btn.click();
text.input();
// 切换风格时需同时更换两个工厂实例,且依然存在混用的可能
ButtonFactory btnFactory2 = new WindowsButtonFactory();
TextFieldFactory textFactory2 = new MacTextFieldFactory(); // 人为错误导致混用
}
}
不足分析:
- 客户端与多个工厂耦合:每增加一种产品类型,客户端就需要多管理一个工厂实例。
- 产品族一致性依然脆弱:无法从设计层面强制保证所有产品属于同一族。
- 切换成本高:切换整个产品族时,客户端必须同步替换所有相关工厂。
2.3 抽象工厂模式重构
抽象工厂模式将同一产品族的创建方法集中在一个工厂接口中,从根本上解决了上述问题。
2.3.1 定义抽象产品接口
// 抽象产品A:按钮
interface Button {
void click();
}
// 抽象产品B:文本框
interface TextField {
void input();
}
2.3.2 定义具体产品类(两个产品族)
// ========== Windows产品族 ==========
class WindowsButton implements Button {
@Override
public void click() {
System.out.println("[Windows] 按钮被点击,呈现扁平直角风格");
}
}
class WindowsTextField implements TextField {
@Override
public void input() {
System.out.println("[Windows] 文本框获取焦点,边框高亮为蓝色");
}
}
// ========== Mac产品族 ==========
class MacButton implements Button {
@Override
public void click() {
System.out.println("[Mac] 按钮被点击,呈现圆角渐变风格");
}
}
class MacTextField implements TextField {
@Override
public void input() {
System.out.println("[Mac] 文本框获取焦点,边框带柔光阴影");
}
}
2.3.3 定义抽象工厂接口
// 抽象工厂:声明创建一族产品的方法
interface UIFactory {
Button createButton(); // 工厂方法1:创建按钮
TextField createTextField(); // 工厂方法2:创建文本框
}
2.3.4 实现具体工厂类
// Windows风格的具体工厂
class WindowsUIFactory implements UIFactory {
@Override
public Button createButton() {
return new WindowsButton(); // 返回Windows按钮
}
@Override
public TextField createTextField() {
return new WindowsTextField(); // 返回Windows文本框
}
}
// Mac风格的具体工厂
class MacUIFactory implements UIFactory {
@Override
public Button createButton() {
return new MacButton();
}
@Override
public TextField createTextField() {
return new MacTextField();
}
}
2.3.5 客户端代码(面向抽象编程)
public class AbstractFactoryClient {
// 客户端仅依赖于抽象工厂和抽象产品接口
private final UIFactory factory;
public AbstractFactoryClient(UIFactory factory) {
this.factory = factory; // 通过构造函数注入具体工厂
}
public void renderDialog() {
Button btn = factory.createButton();
TextField text = factory.createTextField();
btn.click();
text.input();
}
public static void main(String[] args) {
// 根据系统属性或配置决定使用哪个具体工厂
String osName = System.getProperty("os.name").toLowerCase();
UIFactory factory;
if (osName.contains("win")) {
factory = new WindowsUIFactory();
} else {
factory = new MacUIFactory();
}
AbstractFactoryClient client = new AbstractFactoryClient(factory);
client.renderDialog();
// 切换产品族:仅需更换具体工厂实例
System.out.println("\n=== 切换至Mac风格 ===");
client = new AbstractFactoryClient(new MacUIFactory());
client.renderDialog();
}
}
运行结果:
[Windows] 按钮被点击,呈现扁平直角风格
[Windows] 文本框获取焦点,边框高亮为蓝色
=== 切换至Mac风格 ===
[Mac] 按钮被点击,呈现圆角渐变风格
[Mac] 文本框获取焦点,边框带柔光阴影
重构成果:
- 产品族一致性保障:同一个具体工厂返回的所有产品必然属于同一产品族,从根本上杜绝混用。
- 客户端解耦:客户端代码仅依赖
UIFactory、Button、TextField三个抽象接口。 - 切换灵活:运行时通过替换具体工厂实例即可完成整套UI风格的切换。
2.4 抽象工厂模式的变体
2.4.1 可扩展的抽象工厂(利用反射减少子类数量)
当产品族数量庞大时,为每一个产品族编写具体工厂类会导致类爆炸。我们可以利用反射或泛型设计一个通用的可配置工厂。
// 泛型抽象工厂
interface GenericUIFactory {
<T> T create(Class<T> productType);
}
// 基于反射的具体工厂实现
class ConfigurableUIFactory implements GenericUIFactory {
private final String stylePrefix; // 如 "Windows" 或 "Mac"
public ConfigurableUIFactory(String stylePrefix) {
this.stylePrefix = stylePrefix;
}
@Override
@SuppressWarnings("unchecked")
public <T> T create(Class<T> productType) {
try {
// 根据前缀拼接具体实现类名,例如 WindowsButton
String className = stylePrefix + productType.getSimpleName();
Class<?> implClass = Class.forName(className);
return (T) implClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("无法创建产品: " + productType, e);
}
}
}
// 使用示例
public class VariantDemo {
public static void main(String[] args) {
GenericUIFactory factory = new ConfigurableUIFactory("com.example.Windows");
Button btn = factory.create(Button.class);
btn.click();
}
}
2.4.2 结合单例模式管理具体工厂实例
具体工厂通常是无状态的,适合设计为单例以节省资源。
class WindowsUIFactorySingleton {
private static final WindowsUIFactory INSTANCE = new WindowsUIFactory();
private WindowsUIFactorySingleton() {}
public static WindowsUIFactory getInstance() {
return INSTANCE;
}
}
// 更优雅的方式:枚举单例
enum MacUIFactoryEnum implements UIFactory {
INSTANCE;
@Override
public Button createButton() { return new MacButton(); }
@Override
public TextField createTextField() { return new MacTextField(); }
}
三、源码级应用分析
抽象工厂模式在Java生态的诸多核心框架中扮演着关键角色。本节将深入JDK、Spring、MyBatis等源码,剖析其真实落地的精妙之处。
3.1 JDK中的抽象工厂模式
3.1.1 javax.xml.parsers.DocumentBuilderFactory
DOM解析器是处理XML文档的标准API。DocumentBuilderFactory是一个典型的抽象工厂,其newInstance()方法根据系统属性或类路径下的服务配置返回具体的工厂实现(如Apache Xerces的实现类)。
// 抽象工厂角色:DocumentBuilderFactory
public abstract class DocumentBuilderFactory {
public static DocumentBuilderFactory newInstance() {
// 通过服务发现机制加载具体工厂实现
return FactoryFinder.find(DocumentBuilderFactory.class,
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
}
public abstract DocumentBuilder newDocumentBuilder() throws ParserConfigurationException;
// 省略其他配置方法
}
// 抽象产品:DocumentBuilder
public abstract class DocumentBuilder {
public Document parse(InputStream is) throws SAXException, IOException { ... }
// ...
}
// 具体工厂(以Xerces为例)
public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory {
@Override
public DocumentBuilder newDocumentBuilder() {
return new DocumentBuilderImpl(this, attributes);
}
}
分析:DocumentBuilderFactory抽象工厂负责创建DocumentBuilder抽象产品,而具体的解析器实现(Xerces、Crimson等)作为不同的产品族被封装在各自的工厂子类中。客户端通过切换工厂实现即可无缝替换整个XML解析引擎。
3.1.2 javax.xml.transform.TransformerFactory
XSLT转换器的抽象工厂模式与上述完全对称。
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(xslFile));
// Transformer即为抽象产品,其具体实现由底层Xalan或Saxon等引擎提供
3.1.3 java.sql.Connection——数据库访问的抽象工厂
Connection接口是抽象工厂模式的绝佳范例。它负责创建一系列数据库操作相关的产品族对象:Statement、PreparedStatement、CallableStatement、DatabaseMetaData等。不同的数据库驱动(MySQL、Oracle、PostgreSQL)提供各自的Connection实现,从而构成不同的产品族。
// 抽象工厂:Connection
public interface Connection {
Statement createStatement() throws SQLException;
PreparedStatement prepareStatement(String sql) throws SQLException;
CallableStatement prepareCall(String sql) throws SQLException;
// ...
}
// 抽象产品:Statement
public interface Statement {
ResultSet executeQuery(String sql) throws SQLException;
// ...
}
// 具体工厂:以MySQL的ConnectionImpl为例
public class ConnectionImpl implements Connection {
@Override
public Statement createStatement() {
return new StatementImpl(this); // 返回MySQL特定的Statement实现
}
// ...
}
深度剖析:在运行时,DriverManager.getConnection(url)根据URL前缀(如jdbc:mysql://)加载对应的驱动,并返回该驱动的Connection实现。客户端一旦获得Connection对象,后续所有SQL执行对象的创建均通过该抽象工厂完成,从而保证Statement、PreparedStatement等对象与底层数据库协议严格匹配。若混用不同厂商的Connection与Statement,将导致运行时异常。
3.1.4 javax.swing.LookAndFeel与UIDefaults
Swing是抽象工厂模式在GUI领域的旗舰级应用。LookAndFeel抽象类定义了一个getDefaults()方法返回UIDefaults对象,而UIDefaults本质上是一个庞大的注册表,存储了各UI组件的具体类名(产品族配置)。当调用UIManager.setLookAndFeel(String className)时,系统加载对应的LookAndFeel子类(如MetalLookAndFeel、WindowsLookAndFeel),后续创建的JButton、JTextField等组件均依据该产品族配置实例化相应Peer组件。
3.2 Spring框架深度剖析:BeanFactory体系
Spring IoC容器的核心BeanFactory体系是抽象工厂模式在复杂企业级应用中的升华。它并非一个单一的抽象工厂,而是一套分层的、可扩展的产品等级结构。
3.2.1 BeanFactory接口族:抽象产品等级结构
// 根接口:最基础的抽象工厂
public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
boolean containsBean(String name);
// ...
}
// 扩展的抽象工厂:增加了列表、注解等查询能力
public interface ListableBeanFactory extends BeanFactory {
String[] getBeanNamesForType(ResolvableType type);
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType);
}
// 可配置的抽象工厂:增加了配置能力
public interface ConfigurableBeanFactory extends BeanFactory, SingletonBeanRegistry {
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
void setParentBeanFactory(BeanFactory parentBeanFactory);
}
// 自动装配能力的抽象工厂
public interface AutowireCapableBeanFactory extends BeanFactory {
<T> T createBean(Class<T> beanClass);
Object initializeBean(Object existingBean, String beanName);
}
分析:上述接口构成了一个典型的产品等级结构——BeanFactory是抽象产品基类,ListableBeanFactory、ConfigurableBeanFactory、AutowireCapableBeanFactory是不同维度增强的子产品。而Spring提供的具体容器实现(如DefaultListableBeanFactory)则同时实现了这些接口,扮演着具体工厂的角色。
3.2.2 AbstractApplicationContext.refresh()中的产品族切换
ApplicationContext是BeanFactory子接口的扩展,提供了更多企业级特性。AbstractApplicationContext的refresh()方法模板中,obtainFreshBeanFactory()步骤负责创建并配置内部的BeanFactory实例。
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
// ...
// 关键步骤:获取或刷新内部的BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 后续用此beanFactory创建所有Bean
// ...
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory(); // 抽象方法,由子类实现具体创建逻辑
return getBeanFactory();
}
}
// 具体工厂1:基于XML的ApplicationContext
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
// 从XML加载Bean定义,创建基于XML的产品族
}
}
// 具体工厂2:基于注解的ApplicationContext
public class AnnotationConfigApplicationContext extends GenericApplicationContext {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
// 从注解扫描加载Bean定义,创建基于注解的产品族
}
}
产品族切换机制:当开发者使用new ClassPathXmlApplicationContext("beans.xml")或new AnnotationConfigApplicationContext(AppConfig.class)时,实际上选择了不同的具体工厂(产品族)。内部refreshBeanFactory()方法会根据工厂类型加载不同来源的Bean定义,从而生成不同“风味”的Bean对象族,但对客户端而言,它们都统一为ApplicationContext接口。
3.3 MyBatis框架:DataSourceFactory体系
MyBatis的数据源配置支持三种内建类型:UNPOOLED、POOLED、JNDI。这正是抽象工厂模式的体现。
// 抽象工厂
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
// 具体工厂1:非池化数据源工厂
public class UnpooledDataSourceFactory implements DataSourceFactory {
protected DataSource dataSource;
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
@Override
public DataSource getDataSource() {
return dataSource;
}
}
// 具体工厂2:池化数据源工厂
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
// 具体工厂3:JNDI数据源工厂
public class JndiDataSourceFactory implements DataSourceFactory {
private DataSource dataSource;
@Override
public void setProperties(Properties properties) {
// 从JNDI上下文查找数据源
}
}
MyBatis在解析配置文件<dataSource type="POOLED">时,会根据type属性反射实例化对应的DataSourceFactory,从而切换整个数据源产品族。
3.4 java.sql包深度剖析(产品族创建流程图)
为直观展示java.sql.Connection作为抽象工厂的产品族创建过程,以下绘制流程图。
flowchart TB
Start([客户端调用]) --> GetConn[DriverManager.getConnection<br/>根据URL获取Connection]
GetConn --> LoadDriver[加载对应数据库驱动]
LoadDriver --> CreateConn[驱动创建具体Connection实现]
CreateConn --> UseConn[客户端持有Connection抽象工厂]
UseConn --> CreateStmt{调用创建方法}
CreateStmt -->|createStatement| Stmt[返回具体Statement实现]
CreateStmt -->|prepareStatement| PrepStmt[返回具体PreparedStatement实现]
CreateStmt -->|prepareCall| CallStmt[返回具体CallableStatement实现]
Stmt --> End([执行SQL操作])
PrepStmt --> End
CallStmt --> End
classDef driver fill:#fbb,stroke:#333;
classDef product fill:#bfb,stroke:#333;
class LoadDriver,CreateConn driver;
class Stmt,PrepStmt,CallStmt product;
图表解读(不少于200字):
上图展示了java.sql包中抽象工厂模式的运行时协作流程。流程始于客户端调用DriverManager.getConnection(url),该静态方法会遍历已注册的数据库驱动,根据URL前缀(如jdbc:mysql://)匹配相应的驱动实现(如MySQL Connector/J)。驱动负责实例化其专属的Connection实现类(例如com.mysql.cj.jdbc.ConnectionImpl),并将其返回给客户端。此时,客户端获得的Connection对象即是一个具体工厂实例。当客户端调用createStatement()、prepareStatement()等方法时,Connection实现类内部会返回与其自身数据库协议相匹配的具体Statement、PreparedStatement等产品对象。这一机制确保了Statement执行SQL时使用的网络协议、字符集处理、预编译语法均与Connection所代表的数据库会话严格一致。若开发者强行将MySQL的Connection与Oracle的Statement组合使用,必将在运行时抛出ClassCastException或产生不可预知的逻辑错误,这正是抽象工厂模式保证产品族一致性的底层约束在起作用。
四、分布式环境下的抽象工厂模式
随着架构向微服务和云原生演进,抽象工厂模式在分布式场景中焕发出新的生命力。本章将探讨其在现代分布式系统中的典型应用模式。
4.1 微服务架构中的客户端产品族管理
在微服务体系中,一个服务往往需要调用多个下游服务(用户服务、订单服务、库存服务等)。若直接为每个服务编写特定的RPC客户端调用代码,将导致代码重复与治理困难。抽象工厂模式可以将同一套调用风格(如同步HTTP、异步消息、gRPC)下的不同服务客户端封装为一个产品族。
// 抽象产品:用户服务客户端
interface UserServiceClient {
UserDTO getUserById(Long id);
}
// 抽象产品:订单服务客户端
interface OrderServiceClient {
OrderDTO createOrder(OrderRequest request);
}
// 抽象工厂:微服务客户端工厂
interface ServiceClientFactory {
UserServiceClient createUserClient();
OrderServiceClient createOrderClient();
}
// 具体工厂1:基于HTTP/REST的客户端族
class HttpServiceClientFactory implements ServiceClientFactory {
@Override
public UserServiceClient createUserClient() {
return new HttpUserServiceClient(); // 基于RestTemplate或WebClient
}
@Override
public OrderServiceClient createOrderClient() {
return new HttpOrderServiceClient();
}
}
// 具体工厂2:基于gRPC的客户端族
class GrpcServiceClientFactory implements ServiceClientFactory {
@Override
public UserServiceClient createUserClient() {
return new GrpcUserServiceClient(); // 基于gRPC Stub
}
@Override
public OrderServiceClient createOrderClient() {
return new GrpcOrderServiceClient();
}
}
优势:
- 统一技术栈切换:从HTTP迁移至gRPC时,只需更换具体工厂实现。
- 版本兼容管理:可为不同版本的服务接口提供不同的具体工厂,例如
V1ServiceClientFactory和V2ServiceClientFactory。
4.2 多租户SaaS系统的数据源路由
多租户架构中,不同租户的数据可能存储在不同的物理数据库或Schema中。抽象工厂模式结合ThreadLocal可实现优雅的运行时数据源产品族切换。
// 抽象工厂:数据源工厂
interface TenantDataSourceFactory {
DataSource getDataSource();
}
// 具体工厂:每个租户对应一个具体工厂实例
class TenantSpecificDataSourceFactory implements TenantDataSourceFactory {
private final DataSource dataSource;
public TenantSpecificDataSourceFactory(String tenantId) {
// 根据租户ID创建专属数据源(如HikariCP池)
this.dataSource = createDataSourceForTenant(tenantId);
}
@Override
public DataSource getDataSource() { return dataSource; }
}
// 租户上下文持有者
class TenantContext {
private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static void setTenant(String tenantId) { currentTenant.set(tenantId); }
public static String getTenant() { return currentTenant.get(); }
}
// 抽象工厂路由器
class RoutingDataSourceFactory implements TenantDataSourceFactory {
private final Map<String, TenantDataSourceFactory> factoryCache = new ConcurrentHashMap<>();
@Override
public DataSource getDataSource() {
String tenantId = TenantContext.getTenant();
return factoryCache.computeIfAbsent(tenantId,
TenantSpecificDataSourceFactory::new).getDataSource();
}
}
在业务代码中,只需在请求入口设置TenantContext.setTenant(tenantId),后续DAO层通过RoutingDataSourceFactory获取的Connection便自动指向对应租户的数据库,实现产品族的动态切换。
4.3 云原生环境下的多厂商适配
现代应用常需同时支持多家云厂商的对象存储服务(AWS S3、阿里云OSS、腾讯云COS)。抽象工厂模式可屏蔽SDK差异,提供统一的操作接口。
// 抽象产品:对象存储客户端
interface ObjectStorageClient {
void upload(String bucket, String key, InputStream data);
InputStream download(String bucket, String key);
}
// 抽象工厂:云存储工厂
interface CloudStorageFactory {
ObjectStorageClient createClient();
}
// 具体工厂:AWS S3
class AwsS3Factory implements CloudStorageFactory {
@Override
public ObjectStorageClient createClient() {
return new AwsS3ClientAdapter(); // 内部封装AWS SDK
}
}
// 具体工厂:阿里云OSS
class AliyunOssFactory implements CloudStorageFactory {
@Override
public ObjectStorageClient createClient() {
return new AliyunOssClientAdapter();
}
}
结合配置中心(如Nacos),可以在运行时动态切换云厂商而无需重新发布应用。
4.4 分布式配置中心的产品族管理
当系统需要同时支持Apollo、Nacos等多种配置中心时,抽象工厂模式同样适用。
interface ConfigCenterClient {
String getProperty(String key);
}
interface ConfigCenterFactory {
ConfigCenterClient createClient();
}
class ApolloConfigFactory implements ConfigCenterFactory { ... }
class NacosConfigFactory implements ConfigCenterFactory { ... }
4.5 分布式组件切换代码示例
以下示例展示如何利用抽象工厂模式在Redis集群模式与单机模式之间动态切换。
// 抽象产品:Redis客户端
interface RedisClient {
String get(String key);
void set(String key, String value);
}
// 具体产品:单机版
class JedisStandaloneClient implements RedisClient { ... }
// 具体产品:集群版
class JedisClusterClient implements RedisClient { ... }
// 抽象工厂
interface RedisClientFactory {
RedisClient createClient();
}
// 具体工厂
class StandaloneRedisFactory implements RedisClientFactory {
@Override
public RedisClient createClient() {
return new JedisStandaloneClient();
}
}
class ClusterRedisFactory implements RedisClientFactory {
@Override
public RedisClient createClient() {
return new JedisClusterClient();
}
}
// 使用端
public class CacheService {
private final RedisClient redisClient;
public CacheService(RedisClientFactory factory) {
this.redisClient = factory.createClient();
}
// 业务方法...
}
对于消息队列(Kafka vs RocketMQ),可同理设计MessageProducerFactory和MessageConsumerFactory。
4.6 分布式场景下抽象工厂实例的生命周期管理
在分布式环境中,具体工厂实例可能是有状态的(如包含连接池),需要统一管理其生命周期。常见方案如下:
- 结合服务注册中心:将具体工厂实现注册到Nacos/Consul,客户端通过服务发现获取工厂实例。
- 容器化托管:利用Spring IoC容器管理工厂Bean的作用域(如
@Scope("prototype")或自定义Scope)。 - 优雅启停:具体工厂实现
AutoCloseable接口,在应用关闭时释放资源。
interface ManagedFactory extends AutoCloseable, CloudStorageFactory { }
@Component
class AwsS3ManagedFactory implements ManagedFactory {
private final AmazonS3 client;
public AwsS3ManagedFactory() { this.client = AmazonS3ClientBuilder.standard().build(); }
@Override
public ObjectStorageClient createClient() { return new AwsS3ClientAdapter(client); }
@Override
public void close() { client.shutdown(); }
}
五、对比辨析
5.1 抽象工厂模式 vs 工厂方法模式
| 对比维度 | 工厂方法模式 | 抽象工厂模式 |
|---|---|---|
| 产品维度 | 单一产品等级结构 | 多个产品等级结构组成的产品族 |
| 扩展维度 | 易于新增产品(增加新的ConcreteCreator) | 易于新增产品族,但新增产品等级需修改抽象接口 |
| 复杂度 | 较低,每个具体工厂只创建一个产品 | 较高,涉及多产品协作 |
| 典型场景 | 日志框架(Logback、Log4j)的LoggerFactory | UI主题切换、跨数据库DAO层 |
选择依据:当系统中存在多个相互关联且必须协同使用的产品时,必须采用抽象工厂模式;若仅需创建单一类型对象,工厂方法模式更轻量。
5.2 抽象工厂模式 vs 建造者模式
- 抽象工厂关注的是产品族的创建,强调“什么产品被创建”,通常通过方法返回不同产品实例。
- 建造者模式关注的是单个复杂对象的构建过程,强调“如何一步步构建一个产品”,将构建与表示分离。
选用指南:
- 若需要创建一系列具有共同主题的对象(如一套UI组件),用抽象工厂。
- 若需要创建一个参数众多、构造过程复杂的对象(如HttpRequest、Pizza),用建造者。
5.3 抽象工厂模式 vs 原型模式
原型模式同样可用于产品族切换:通过克隆预先配置好的原型实例来获得新对象。例如,预先保存一套Windows按钮和文本框的原型对象,切换时只需克隆这些原型。
| 模式 | 优势 | 劣势 |
|---|---|---|
| 抽象工厂 | 类型安全,编译期检查;便于扩展新的产品族 | 新增产品等级结构需修改抽象接口 |
| 原型模式 | 运行时动态配置,减少子类数量;新增产品等级无需修改接口 | 克隆操作可能涉及深拷贝复杂性;类型安全性较弱 |
当产品族种类繁多且经常变化时,原型模式更为灵活;当产品族相对固定且需要严格类型检查时,抽象工厂是更稳健的选择。
5.4 抽象工厂模式与依赖注入(DI)的关系
同源性:从设计理念上看,依赖注入容器(如Spring IoC)本质上就是一个超级可配置的抽象工厂。它根据配置元数据(XML、注解)动态地创建并装配一族相互依赖的对象。
差异:
- 抽象工厂模式:显式定义了创建产品族的接口,客户端主动调用工厂方法获取对象。
- DI容器:通过控制反转,容器主动向客户端注入所需对象,客户端甚至不知道工厂的存在。
融合实践:在Spring应用中,我们常将具体工厂实现注册为Bean,然后通过@Autowired注入抽象工厂接口,实现运行时产品族的切换。
六、适用场景分析
6.1 跨平台UI组件库开发
场景:Swing、JavaFX、Eclipse SWT均采用抽象工厂模式实现LookAndFeel(外观)切换。
技术理由:UI组件(按钮、菜单、滚动条)必须属于同一视觉风格。抽象工厂将同一风格的组件创建封装在单一工厂中,客户端无需感知具体AWT/Swing对等组件。
6.2 多数据库适配层(DAO层)
场景:企业应用需要兼容MySQL、Oracle、PostgreSQL,并可在配置文件中切换。
技术理由:不同数据库的SQL方言、分页语法、序列生成机制各不相同。DAO层的Connection、Statement实现必须来自同一厂商。抽象工厂(如Spring的DataSourceTransactionManager结合DataSource)可保证事务管理与连接对象的一致性。
6.3 配置驱动框架
场景:Spring的BeanFactory体系,MyBatis的DataSourceFactory体系。
技术理由:框架需要根据配置文件动态决定创建何种类型的对象族(XML配置的Bean vs 注解配置的Bean,池化数据源 vs 非池化数据源)。抽象工厂提供了统一的扩展点。
6.4 游戏开发中的主题与皮肤系统
场景:游戏角色、场景、UI控件在不同主题(万圣节、圣诞节、科幻风)下拥有不同外观族。
技术理由:保证同一主题下的所有视觉元素风格统一,切换主题时仅需替换一个工厂实例。
6.5 消息中间件客户端
场景:应用需支持Kafka、RabbitMQ、RocketMQ的消息生产和消费。
技术理由:不同MQ的Producer和Consumer API完全不同。抽象工厂可封装MQProducer和MQConsumer的创建,使业务代码与具体MQ解耦。
6.6 测试环境与生产环境的切换
场景:单元测试时使用Mock对象族,集成测试/生产环境使用真实对象族。
技术理由:通过依赖注入不同的具体工厂,可在不修改业务代码的前提下实现测试替身的全面替换。
6.7 不适用场景警示
- 产品族稳定但产品等级频繁扩展:例如,UI组件库的组件类型(Button、TextField)固定,但经常需要新增一套视觉风格(产品族)。此时抽象工厂是合适的。反之,若产品族(如Windows/Mac)固定,而需要频繁新增组件类型(如新增Chart、Slider),则工厂方法模式更合适,因为新增产品等级会导致抽象工厂接口及其所有子类的修改,违背开闭原则。
七、面试题精选与专家级解答
1. 什么是产品族和产品等级结构?请用实际例子说明
参考答案:
- 产品等级结构:指同一类型产品所构成的继承体系。例如,
Button接口与WindowsButton、MacButton实现类之间就形成了一个产品等级结构。它描述的是产品的纵向分类。 - 产品族:指由同一个工厂生产、位于不同产品等级结构中的一组相关产品。例如,
WindowsButton和WindowsTextField同属于Windows产品族,它们虽然在不同的产品等级结构中,但必须协同工作以保证风格一致。它描述的是产品的横向关联。
2. 抽象工厂模式与工厂方法模式的核心区别是什么?如何根据需求选择?
参考答案:
- 核心区别:工厂方法模式针对单一产品等级结构,一个工厂只创建一个产品;抽象工厂模式针对多个产品等级结构,一个工厂创建一族相关的产品。
- 选择依据:如果系统中存在多个相互依赖的对象,且它们必须成套使用(如UI组件、数据库连接与语句对象),则必须采用抽象工厂模式以保证产品族的一致性。如果只需创建单一类型对象,且未来主要变化在于新增该类型的子类,则工厂方法模式更为轻量且符合开闭原则。
3. JDK中哪些地方使用了抽象工厂模式?请至少列举三个并分析其实现细节
参考答案:
javax.xml.parsers.DocumentBuilderFactory:抽象工厂,newInstance()方法通过服务发现返回具体工厂实现。DocumentBuilder是抽象产品,不同解析器(Xerces)提供具体产品。java.sql.Connection:作为抽象工厂,创建Statement、PreparedStatement等产品族。不同驱动(MySQL、Oracle)提供不同的Connection实现类。javax.swing.LookAndFeel:Swing的抽象工厂,其getDefaults()返回UIDefaults,后者定义了各UI组件对应的具体实现类名。UIManager负责加载具体LookAndFeel子类,从而切换整个组件族。- (可选)
java.awt.Toolkit:抽象工厂,用于创建与本地平台交互的对等组件(Peer)。
4. Spring的BeanFactory体系是如何体现抽象工厂模式的?谈谈你的理解
参考答案:
Spring的BeanFactory接口及其子接口(ListableBeanFactory、ConfigurableBeanFactory等)构成了一个丰富的产品等级结构。BeanFactory作为根接口,定义了获取Bean的基本方法。DefaultListableBeanFactory是具体工厂实现,它同时实现了上述多个接口,负责创建和管理Bean实例。而ApplicationContext接口则是在BeanFactory基础上的又一层次抽象,ClassPathXmlApplicationContext和AnnotationConfigApplicationContext可视为不同的具体工厂,它们决定了Bean定义的加载方式(XML vs 注解),从而产生不同配置来源的Bean产品族。整个体系完美体现了“面向接口编程”与“产品族管理”的抽象工厂思想。
5. 抽象工厂模式如何保证产品族内对象的一致性?如果混用产品族会导致什么问题?
参考答案:
抽象工厂模式通过将同一产品族的所有创建方法集中在单一具体工厂类中来保证一致性。客户端必须通过同一个具体工厂实例来获取所有相关产品,因此不可能出现“从工厂A获取按钮,从工厂B获取文本框”的情况。如果开发者绕开工厂直接new具体类,或者手动混用不同工厂的返回值,则会导致产品族混用。其后果包括但不限于:UI风格错乱、数据库连接与Statement协议不匹配引发运行时异常、业务逻辑依赖的内部状态不一致等。
6. 新增一个产品等级结构时,抽象工厂模式需要修改哪些代码?这违背了开闭原则吗?
参考答案: 新增一个产品等级结构(例如在Button、TextField之外新增Chart组件)时,需要修改:
- 抽象工厂接口,增加
createChart()方法。 - 所有具体工厂实现类,增加对应的创建方法实现。
这违背了开闭原则(对扩展开放,对修改封闭),因为抽象接口和已有实现类都需要修改。这是抽象工厂模式的固有缺点。弥补方案包括:
- 使用带参数的创建方法(如
create(Class<T> type))并结合反射,避免接口修改。 - 当产品等级结构频繁变化时,考虑使用原型模式或建造者模式替代。
7. 如何用抽象工厂模式设计一个支持多数据库切换的DAO层?请画出类图并说明
flowchart TB
subgraph 抽象层
AF[DatabaseFactory<br/>抽象数据库工厂]
Conn[Connection<br/>抽象连接]
Cmd[Command<br/>抽象命令]
end
subgraph 具体工厂与产品族
MySQLF[MySQLFactory]
MySQLConn[MySQLConnection]
MySQLCmd[MySQLCommand]
PgF[PostgreSQLFactory]
PgConn[PostgreSQLConnection]
PgCmd[PostgreSQLCommand]
end
AF -->|createConnection| Conn
AF -->|createCommand| Cmd
MySQLF -->|实现| AF
PgF -->|实现| AF
MySQLF -->|创建| MySQLConn
MySQLF -->|创建| MySQLCmd
PgF -->|创建| PgConn
PgF -->|创建| PgCmd
MySQLConn -->|实现| Conn
MySQLCmd -->|实现| Cmd
PgConn -->|实现| Conn
PgCmd -->|实现| Cmd
classDef abstract fill:#f9f,stroke:#333;
class AF,Conn,Cmd abstract;
说明:DatabaseFactory抽象工厂声明了创建Connection和Command的方法。MySQLFactory和PostgreSQLFactory分别实现了这些方法,返回各自数据库的Connection和Command具体产品。DAO层通过注入DatabaseFactory来工作,切换数据库时仅需更换注入的具体工厂实例,DAO层代码零改动。
8. 抽象工厂模式与依赖注入容器(如Spring IoC)在解决对象创建问题时有什么异同?
参考答案:
- 相同点:二者都将对象的创建与使用分离,客户端面向抽象编程,不关心具体实现类。DI容器本质上是抽象工厂模式的极致化应用。
- 不同点:
- 主动性:抽象工厂模式中,客户端主动调用工厂方法获取对象;DI容器中,客户端被动接收容器注入的对象。
- 管理范围:抽象工厂通常只管理一个产品族的创建;DI容器管理整个应用中所有Bean的生命周期与依赖关系。
- 配置方式:抽象工厂通常通过硬编码或简单配置选择具体工厂;DI容器支持XML、注解、Java Config等丰富的元数据定义。
9. 抽象工厂模式中的工厂类是否应该设计为单例?为什么?
参考答案: 应该设计为单例。理由如下:
- 无状态性:绝大多数具体工厂类不包含成员变量,仅提供创建方法,因此线程安全且无状态,适合单例复用。
- 资源节约:避免反复创建工厂对象带来的开销。
- 管理便利:单例工厂便于统一管理和切换。
Spring中默认将工厂Bean注册为单例作用域正是基于此理。
10. 在微服务架构中,如何利用抽象工厂模式管理多服务客户端的版本兼容问题?
参考答案: 可以为不同API版本的服务提供不同的具体工厂。例如:
interface ServiceClientFactory {
UserServiceClient createUserClient();
OrderServiceClient createOrderClient();
}
class ServiceClientFactoryV1 implements ServiceClientFactory { ... } // 调用v1端点
class ServiceClientFactoryV2 implements ServiceClientFactory { ... } // 调用v2端点
客户端可根据请求头中的版本号或服务发现的元数据选择对应的工厂实例。结合服务网格(如Istio)可实现更细粒度的流量切换。此外,可利用抽象工厂的变体,将客户端实例的创建委托给具有版本感知能力的工厂,从而实现平滑升级。
八、附录:所有图表汇总
图a:抽象工厂模式标准UML类图(见1.2节)
图b:工厂方法模式演进为抽象工厂模式的对比类图
flowchart LR
subgraph "工厂方法模式"
direction TB
FM_Creator["Creator<br>+factoryMethod()"]
FM_Product["Product"]
FM_ConcreteCreator["ConcreteCreator<br>+factoryMethod()"]
FM_ConcreteProduct["ConcreteProduct"]
FM_ConcreteCreator -->|"创建"| FM_ConcreteProduct
FM_ConcreteProduct -->|"实现"| FM_Product
FM_ConcreteCreator -->|"实现"| FM_Creator
end
subgraph "抽象工厂模式"
direction TB
AF_Factory["AbstractFactory<br>+createA()<br>+createB()"]
AF_ProductA["ProductA"]
AF_ProductB["ProductB"]
AF_ConcreteFactory["ConcreteFactory<br>+createA()<br>+createB()"]
AF_ProductA1["ProductA1"]
AF_ProductB1["ProductB1"]
AF_ConcreteFactory -->|"创建"| AF_ProductA1
AF_ConcreteFactory -->|"创建"| AF_ProductB1
AF_ProductA1 -->|"实现"| AF_ProductA
AF_ProductB1 -->|"实现"| AF_ProductB
AF_ConcreteFactory -->|"实现"| AF_Factory
end
FM_Creator -.->|"扩展为"| AF_Factory
FM_Product -.->|"分裂为多个等级"| AF_ProductA
FM_Product -.->|"分裂为多个等级"| AF_ProductB
说明:左侧工厂方法模式中,一个具体工厂只负责创建一个具体产品,适用于单产品等级结构的扩展。当系统演化出多个相互依赖的产品时(如UI库同时包含按钮和文本框),工厂方法模式迫使客户端与多个工厂耦合。右侧抽象工厂模式将多个工厂方法收敛至单一接口,实现产品族的统一创建与切换,从而弥补了工厂方法在处理产品族时的不足。
图c:Spring BeanFactory体系抽象工厂结构图
flowchart TB
subgraph "抽象工厂接口层"
BF["BeanFactory<br>+getBean()"]
LBF["ListableBeanFactory<br>+getBeanNamesForType()"]
CBF["ConfigurableBeanFactory<br>+addBeanPostProcessor()"]
ABF["AutowireCapableBeanFactory<br>+createBean()"]
end
subgraph "具体工厂实现层"
DLBF["DefaultListableBeanFactory"]
end
subgraph "高层抽象及应用"
AC["ApplicationContext<br>+getBean() + 事件发布等"]
XMLAC["ClassPathXmlApplicationContext"]
ANNAC["AnnotationConfigApplicationContext"]
end
LBF -->|"继承"| BF
CBF -->|"继承"| BF
ABF -->|"继承"| BF
DLBF -->|"实现"| LBF
DLBF -->|"实现"| CBF
DLBF -->|"实现"| ABF
AC -->|"持有"| DLBF
XMLAC -->|"实现"| AC
ANNAC -->|"实现"| AC
classDef interface fill:#fbb,stroke:#333,stroke-width:1px;
classDef concrete fill:#bbf,stroke:#333,stroke-width:1px;
class BF,LBF,CBF,ABF,AC interface;
class DLBF,XMLAC,ANNAC concrete;
解读:该图揭示了Spring IoC容器的抽象工厂本质。BeanFactory作为根接口定义最基础的获取Bean方法。其三个子接口分别扩展了列表查询、配置管理和自动装配能力,它们共同构成了一个产品等级结构。DefaultListableBeanFactory作为核心具体工厂,同时实现了所有这些接口,具备完整的产品族创建能力。ApplicationContext虽未直接继承DefaultListableBeanFactory,但内部持有一个该工厂实例,并通过ClassPathXmlApplicationContext和AnnotationConfigApplicationContext等不同子类来切换Bean定义的加载方式(即切换产品族)。
图d:DAO层多数据库适配的抽象工厂应用架构图
flowchart TB
subgraph "业务层"
Service["UserService"]
end
subgraph "抽象层"
Factory["DatabaseFactory<br>+getConnection()<br>+getCommand()"]
Conn["Connection<br>+executeQuery()"]
Cmd["Command<br>+setParameter()"]
end
subgraph "MySQL产品族"
MySQLF["MySQLFactory"]
MySQLConn["MySQLConnection"]
MySQLCmd["MySQLCommand"]
end
subgraph "PostgreSQL产品族"
PgF["PostgreSQLFactory"]
PgConn["PGConnection"]
PgCmd["PGCommand"]
end
Service --> Factory
Factory --> Conn
Factory --> Cmd
MySQLF --> MySQLConn
MySQLF --> MySQLCmd
PgF --> PgConn
PgF --> PgCmd
MySQLConn --> Conn
MySQLCmd --> Cmd
PgConn --> Conn
PgCmd --> Cmd
Config["配置中心/环境变量"] -.->|"决定实例化"| MySQLF
Config -.->|"或"| PgF
classDef default fill:#f9f9f9,stroke:#333,stroke-width:1px;
解读:业务层UserService仅依赖DatabaseFactory抽象接口。系统启动时根据配置(如db.type=mysql)决定实例化MySQLFactory还是PostgreSQLFactory并注入给UserService。当UserService调用factory.getConnection()和factory.getCommand()时,实际获取到的是与所选数据库严格匹配的Connection和Command实现。整个DAO层代码无需任何数据库相关的if-else判断,实现了“发现一个变化,只需修改一处”的良好设计。
图e:java.sql.Connection作为抽象工厂的产品族创建流程图(见3.4节)
图f:微服务多客户端产品族的抽象工厂架构图
flowchart TB
subgraph "服务消费者"
OrderProcessor["订单处理服务"]
end
subgraph "抽象工厂层"
SCF["ServiceClientFactory<br>+createUserClient()<br>+createInventoryClient()"]
end
subgraph "HTTP产品族"
HFactory["HttpServiceClientFactory"]
HUser["HttpUserClient"]
HInv["HttpInventoryClient"]
end
subgraph "gRPC产品族"
GFactory["GrpcServiceClientFactory"]
GUser["GrpcUserClient"]
GInv["GrpcInventoryClient"]
end
OrderProcessor --> SCF
SCF -->|"创建"| HFactory
SCF -->|"创建"| GFactory
HFactory -->|"创建"| HUser
HFactory -->|"创建"| HInv
GFactory -->|"创建"| GUser
GFactory -->|"创建"| GInv
Registry["服务注册中心"] -.->|"根据版本策略选择"| HFactory
Registry -.->|"或"| GFactory
classDef default fill:#f9f9f9,stroke:#333,stroke-width:1px;
解读:在微服务架构中,订单处理服务OrderProcessor需要调用用户服务和库存服务。通过ServiceClientFactory抽象工厂,它无需关心底层是使用HTTP REST还是gRPC协议。HttpServiceClientFactory和GrpcServiceClientFactory分别创建对应协议栈的客户端实现,它们各自返回的UserClient和InventoryClient产品在序列化方式、超时控制、负载均衡策略上保持内在一致。结合服务注册中心(如Consul、Nacos),系统可以根据服务实例的元数据标签(如protocol=http2)动态选择具体工厂,从而在不中断业务的情况下完成协议升级或A/B测试。
九、结语
抽象工厂模式作为创建型模式的集大成者,其核心价值在于为产品族的一致性创建提供了优雅的解决方案。从经典的UI主题切换,到现代微服务与云原生架构中的多客户端适配,该模式始终焕发着强大的生命力。作为Java领域的专家,深入理解抽象工厂模式不仅是应对面试的利器,更是构建高内聚、低耦合、可扩展企业级系统的必备思维框架。希望本文能帮助读者在架构设计与代码重构中游刃有余,真正掌握这一模式的精髓。
全文完