设计模式:抽象工厂模式

欢迎你来读这篇博客,这篇博客主要是关于抽象工厂模式
其中包括抽象工厂模式的核心思想、适用场景、优缺点、与工厂方法模式的区别,以及一个贴近 Java 后端开发的多云文件存储适配案例。

序言

前面已经聊过简单工厂模式和工厂方法模式。

简单工厂模式解决的是:

把对象创建逻辑集中起来,不让业务代码到处 new 对象。

工厂方法模式解决的是:

把对象创建逻辑交给具体工厂,让新增产品时尽量通过扩展新类完成,而不是修改旧工厂。

那抽象工厂模式解决什么问题?

一句话:

抽象工厂模式解决的是“一组相关对象”的创建问题。

工厂方法通常关注“创建一个产品”。

抽象工厂关注的是“创建一个产品族”。

比如你不是只要一个按钮,而是要一整套 UI 组件:

  • Windows 风格按钮;
  • Windows 风格输入框;
  • Windows 风格弹窗;

或者:

  • Mac 风格按钮;
  • Mac 风格输入框;
  • Mac 风格弹窗。

这一整套风格必须匹配。不能按钮是 Windows 风格,输入框是 Mac 风格,弹窗又是 Linux 风格。那不是系统,是拼多多盲盒。

在 Java 后端开发中,这种场景也很多。例如多云存储适配:

  • 阿里云 OSS:上传器、下载器、签名器;
  • 腾讯云 COS:上传器、下载器、签名器;
  • MinIO:上传器、下载器、签名器。

这些对象通常需要成套使用,这就是抽象工厂模式非常适合的场景。

正文

chapter 1:什么是抽象工厂模式

抽象工厂模式,英文是 Abstract Factory Pattern,属于创建型设计模式,也是 GoF 23 种经典设计模式之一。

它提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

换句话说:

抽象工厂不是只生产一个产品,而是生产一整套产品族。

经典结构一般包括以下角色:

  1. 抽象工厂:声明创建一组产品的方法。
  2. 具体工厂:实现抽象工厂,创建某一个产品族中的具体产品。
  3. 抽象产品:定义某类产品的公共接口。
  4. 具体产品:实现抽象产品接口,属于某一个具体产品族。
  5. 客户端:只依赖抽象工厂和抽象产品,不依赖具体产品类。

抽象工厂模式的重点不在于“工厂”两个字,而在于“产品族”。

chapter 2:什么是产品族

理解抽象工厂模式,必须先理解两个概念:

  • 产品等级结构;
  • 产品族。

1. 产品等级结构

产品等级结构指的是同一类产品的不同实现。

例如文件存储系统中,有一类产品叫“上传器”:

  • OSS 上传器;
  • COS 上传器;
  • MinIO 上传器。

它们都属于上传器这个产品等级结构。

再比如有一类产品叫“下载器”:

  • OSS 下载器;
  • COS 下载器;
  • MinIO 下载器。

它们都属于下载器这个产品等级结构。

2. 产品族

产品族指的是同一个平台、同一个风格、同一个技术体系下的一整套产品。

例如:

  • OSS 产品族:OSS 上传器、OSS 下载器、OSS 签名器;
  • COS 产品族:COS 上传器、COS 下载器、COS 签名器;
  • MinIO 产品族:MinIO 上传器、MinIO 下载器、MinIO 签名器。

可以这样理解:

1
2
产品等级结构:按“功能类型”纵向看
产品族:按“平台体系”横向看

用表格看更清楚:

产品族 / 产品等级 上传器 下载器 签名器
OSS 产品族 OssUploader OssDownloader OssSigner
COS 产品族 CosUploader CosDownloader CosSigner
MinIO 产品族 MinioUploader MinioDownloader MinioSigner

抽象工厂模式要解决的就是:

如何创建同一个产品族下的一整套对象,并保证它们能够匹配使用。

chapter 3:为什么需要抽象工厂模式

假设我们正在开发一个文件系统模块,系统未来可能支持:

  • 阿里云 OSS;
  • 腾讯云 COS;
  • MinIO;
  • 华为云 OBS;
  • 本地文件系统。

每一种存储平台都需要一组能力:

  • 文件上传;
  • 文件下载;
  • 临时访问 URL 签名;
  • 删除文件;
  • 查询文件元数据。

如果我们直接在业务代码里判断平台类型,就可能写成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class FileService {

public void upload(String storageType, String filename, byte[] content) {
if ("oss".equals(storageType)) {
OssUploader uploader = new OssUploader();
uploader.upload(filename, content);
return;
}

if ("cos".equals(storageType)) {
CosUploader uploader = new CosUploader();
uploader.upload(filename, content);
return;
}

if ("minio".equals(storageType)) {
MinioUploader uploader = new MinioUploader();
uploader.upload(filename, content);
return;
}

throw new IllegalArgumentException("Unsupported storage type: " + storageType);
}
}

如果只是上传,勉强还能忍。

但是一旦业务里同时需要上传、下载、签名、删除,就会变成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if ("oss".equals(storageType)) {
OssUploader uploader = new OssUploader();
OssDownloader downloader = new OssDownloader();
OssUrlSigner signer = new OssUrlSigner();
}

if ("cos".equals(storageType)) {
CosUploader uploader = new CosUploader();
CosDownloader downloader = new CosDownloader();
CosUrlSigner signer = new CosUrlSigner();
}

if ("minio".equals(storageType)) {
MinioUploader uploader = new MinioUploader();
MinioDownloader downloader = new MinioDownloader();
MinioUrlSigner signer = new MinioUrlSigner();
}

这时问题就很明显:

  • 业务代码耦合大量具体实现类;
  • 平台判断逻辑散落在各处;
  • 新增存储平台时,需要修改大量代码;
  • 很容易混用不同产品族的对象;
  • 代码越来越难维护。

抽象工厂模式的思路是:

为每一种存储平台提供一个具体工厂,由这个工厂创建该平台下的一整套组件。

例如:

  • OssStorageFactory 创建 OSS 上传器、OSS 下载器、OSS 签名器;
  • CosStorageFactory 创建 COS 上传器、COS 下载器、COS 签名器;
  • MinioStorageFactory 创建 MinIO 上传器、MinIO 下载器、MinIO 签名器。

这样,业务层只依赖 StorageFactory,不直接依赖具体平台实现。

chapter 4:抽象工厂模式的结构

抽象工厂模式可以用下面的结构理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
                         ┌────────────────────┐
│ StorageFactory │
│ createUploader() │
│ createDownloader() │
│ createUrlSigner() │
└──────────▲─────────┘

┌───────────────────────┼───────────────────────┐
│ │ │
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
│ OssStorageFactory │ │ CosStorageFactory │ │ MinioStorageFactory│
└────────────────────┘ └────────────────────┘ └────────────────────┘


┌──────────────┐ ┌────────────────┐ ┌──────────────┐
│ FileUploader │ │ FileDownloader │ │ UrlSigner │
└──────▲───────┘ └───────▲────────┘ └──────▲───────┘
│ │ │
┌─────┼─────┐ ┌─────┼─────┐ ┌─────┼─────┐
│ │ │ │ │ │ │ │ │
OSS COS MinIO OSS COS MinIO OSS COS MinIO
上传 上传 上传 下载 下载 下载 签名 签名 签名

抽象工厂负责定义“一整套产品怎么创建”。

具体工厂负责创建“某一个产品族里的所有产品”。

chapter 5:案例背景:多云文件存储适配

下面设计一个多云文件存储模块。

需求如下:

  1. 系统支持多种文件存储平台;
  2. 每个平台都提供上传、下载、生成临时访问 URL 的能力;
  3. 业务层不直接依赖 OSS、COS、MinIO 等具体实现;
  4. 新增存储平台时,尽量不修改业务代码;
  5. 同一个请求中使用的上传器、下载器、签名器必须来自同一个平台。

为了简化示例,这里不引入真实 SDK,只用打印日志模拟平台调用。

chapter 6:定义抽象产品

先定义三个抽象产品接口。

1. 文件上传器

1
2
3
4
public interface FileUploader {

String upload(String filename, byte[] content);
}

2. 文件下载器

1
2
3
4
public interface FileDownloader {

byte[] download(String fileKey);
}

3. URL 签名器

1
2
3
4
public interface UrlSigner {

String sign(String fileKey, long expireSeconds);
}

这三个接口分别代表三个产品等级结构:

  • 上传器;
  • 下载器;
  • 签名器。

chapter 7:定义 OSS 产品族

OSS 产品族包含三个具体产品。

1. OSS 上传器

1
2
3
4
5
6
7
8
public class OssFileUploader implements FileUploader {

@Override
public String upload(String filename, byte[] content) {
System.out.println("使用阿里云 OSS 上传文件:" + filename);
return "oss://" + filename;
}
}

2. OSS 下载器

1
2
3
4
5
6
7
8
public class OssFileDownloader implements FileDownloader {

@Override
public byte[] download(String fileKey) {
System.out.println("使用阿里云 OSS 下载文件:" + fileKey);
return new byte[0];
}
}

3. OSS URL 签名器

1
2
3
4
5
6
7
8
public class OssUrlSigner implements UrlSigner {

@Override
public String sign(String fileKey, long expireSeconds) {
System.out.println("使用阿里云 OSS 生成临时访问 URL:" + fileKey);
return "https://oss.example.com/" + fileKey + "?expire=" + expireSeconds;
}
}

chapter 8:定义 MinIO 产品族

MinIO 产品族也包含三个具体产品。

1. MinIO 上传器

1
2
3
4
5
6
7
8
public class MinioFileUploader implements FileUploader {

@Override
public String upload(String filename, byte[] content) {
System.out.println("使用 MinIO 上传文件:" + filename);
return "minio://" + filename;
}
}

2. MinIO 下载器

1
2
3
4
5
6
7
8
public class MinioFileDownloader implements FileDownloader {

@Override
public byte[] download(String fileKey) {
System.out.println("使用 MinIO 下载文件:" + fileKey);
return new byte[0];
}
}

3. MinIO URL 签名器

1
2
3
4
5
6
7
8
public class MinioUrlSigner implements UrlSigner {

@Override
public String sign(String fileKey, long expireSeconds) {
System.out.println("使用 MinIO 生成临时访问 URL:" + fileKey);
return "https://minio.example.com/" + fileKey + "?expire=" + expireSeconds;
}
}

chapter 9:定义抽象工厂

现在定义抽象工厂。

1
2
3
4
5
6
7
8
public interface StorageFactory {

FileUploader createUploader();

FileDownloader createDownloader();

UrlSigner createUrlSigner();
}

这个接口的重点是:它不是只创建一个对象,而是创建一组相关对象。

也就是说,StorageFactory 代表的是一个完整的文件存储产品族工厂。

chapter 10:定义具体工厂

1. OSS 工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class OssStorageFactory implements StorageFactory {

@Override
public FileUploader createUploader() {
return new OssFileUploader();
}

@Override
public FileDownloader createDownloader() {
return new OssFileDownloader();
}

@Override
public UrlSigner createUrlSigner() {
return new OssUrlSigner();
}
}

2. MinIO 工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MinioStorageFactory implements StorageFactory {

@Override
public FileUploader createUploader() {
return new MinioFileUploader();
}

@Override
public FileDownloader createDownloader() {
return new MinioFileDownloader();
}

@Override
public UrlSigner createUrlSigner() {
return new MinioUrlSigner();
}
}

这样就保证了:

  • OSS 工厂只创建 OSS 产品族;
  • MinIO 工厂只创建 MinIO 产品族;
  • 不会出现 OSS 上传器搭配 MinIO 签名器这种混乱情况。

chapter 11:客户端使用

业务服务可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class FileStorageService {

private final StorageFactory storageFactory;

public FileStorageService(StorageFactory storageFactory) {
this.storageFactory = storageFactory;
}

public String uploadAndCreateUrl(String filename, byte[] content) {
FileUploader uploader = storageFactory.createUploader();
UrlSigner urlSigner = storageFactory.createUrlSigner();

String fileKey = uploader.upload(filename, content);

return urlSigner.sign(fileKey, 3600);
}

public byte[] download(String fileKey) {
FileDownloader downloader = storageFactory.createDownloader();

return downloader.download(fileKey);
}
}

客户端调用:

1
2
3
4
5
6
7
8
9
10
11
12
public class Client {

public static void main(String[] args) {
StorageFactory factory = new OssStorageFactory();

FileStorageService fileStorageService = new FileStorageService(factory);

String url = fileStorageService.uploadAndCreateUrl("report.pdf", new byte[0]);

System.out.println("临时访问 URL:" + url);
}
}

如果要切换成 MinIO,只需要替换具体工厂:

1
2
3
StorageFactory factory = new MinioStorageFactory();

FileStorageService fileStorageService = new FileStorageService(factory);

业务服务 FileStorageService 不需要修改。

这就是抽象工厂模式想达到的效果。

chapter 12:使用配置选择具体工厂

真实项目中,具体使用哪个存储平台通常来自配置。

比如:

1
2
storage:
type: minio

可以定义一个简单的工厂选择器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StorageFactoryProvider {

public static StorageFactory getFactory(String type) {
if ("oss".equalsIgnoreCase(type)) {
return new OssStorageFactory();
}

if ("minio".equalsIgnoreCase(type)) {
return new MinioStorageFactory();
}

throw new IllegalArgumentException("Unsupported storage type: " + type);
}
}

调用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Client {

public static void main(String[] args) {
String storageType = "minio";

StorageFactory factory = StorageFactoryProvider.getFactory(storageType);

FileStorageService service = new FileStorageService(factory);

String url = service.uploadAndCreateUrl("avatar.png", new byte[0]);

System.out.println(url);
}
}

这里你可能会发现,又出现了 if else

这不矛盾。

抽象工厂模式关注的是“一整套产品族如何创建”。

至于“选择哪个具体工厂”,可以由配置、Spring 容器、SPI、注册表、枚举、反射等方式完成。

chapter 13:在 Spring Boot 中落地

在 Spring Boot 项目中,我们可以把具体工厂交给 Spring 容器管理。

先定义抽象工厂接口:

1
2
3
4
5
6
7
8
9
10
public interface StorageFactory {

String type();

FileUploader createUploader();

FileDownloader createDownloader();

UrlSigner createUrlSigner();
}

OSS 工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component
public class OssStorageFactory implements StorageFactory {

@Override
public String type() {
return "oss";
}

@Override
public FileUploader createUploader() {
return new OssFileUploader();
}

@Override
public FileDownloader createDownloader() {
return new OssFileDownloader();
}

@Override
public UrlSigner createUrlSigner() {
return new OssUrlSigner();
}
}

MinIO 工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component
public class MinioStorageFactory implements StorageFactory {

@Override
public String type() {
return "minio";
}

@Override
public FileUploader createUploader() {
return new MinioFileUploader();
}

@Override
public FileDownloader createDownloader() {
return new MinioFileDownloader();
}

@Override
public UrlSigner createUrlSigner() {
return new MinioUrlSigner();
}
}

然后定义工厂路由器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
public class StorageFactoryRouter {

private final Map<String, StorageFactory> factoryMap;

public StorageFactoryRouter(List<StorageFactory> factories) {
this.factoryMap = factories.stream()
.collect(Collectors.toUnmodifiableMap(StorageFactory::type, factory -> factory));
}

public StorageFactory getFactory(String type) {
StorageFactory factory = factoryMap.get(type);

if (factory == null) {
throw new IllegalArgumentException("Unsupported storage type: " + type);
}

return factory;
}
}

业务服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
public class FileStorageService {

private final StorageFactoryRouter storageFactoryRouter;

public FileStorageService(StorageFactoryRouter storageFactoryRouter) {
this.storageFactoryRouter = storageFactoryRouter;
}

public String uploadAndCreateUrl(String storageType, String filename, byte[] content) {
StorageFactory factory = storageFactoryRouter.getFactory(storageType);

FileUploader uploader = factory.createUploader();
UrlSigner urlSigner = factory.createUrlSigner();

String fileKey = uploader.upload(filename, content);

return urlSigner.sign(fileKey, 3600);
}
}

这种写法更贴近真实 Spring Boot 项目。

它综合使用了:

  • 抽象工厂思想;
  • Spring Bean 自动发现;
  • Map 路由;
  • 面向接口编程;
  • 开闭原则。

新增一个华为云 OBS 存储平台时,只需要新增:

  • ObsFileUploader
  • ObsFileDownloader
  • ObsUrlSigner
  • ObsStorageFactory

然后加上 @Component,原来的 FileStorageService 不需要修改。

chapter 14:抽象工厂模式和工厂方法模式的区别

这两个模式很容易混。

可以用一句话区分:

工厂方法创建一个产品,抽象工厂创建一组产品。

对比如下:

对比项 工厂方法模式 抽象工厂模式
关注点 单个产品的创建 一组相关产品的创建
工厂方法数量 通常一个 通常多个
产品关系 产品之间不一定有关联 产品之间属于同一个产品族
扩展产品等级 较容易 较困难,需要改抽象工厂接口
扩展产品族 可以 非常适合
复杂度 中等 较高
典型场景 多种通知发送器、多种解析器 多云存储组件族、多数据库组件族、多 UI 风格组件族

举个例子。

如果你只需要创建不同的通知发送器:

  • 邮件通知;
  • 短信通知;
  • 站内信通知;

更适合工厂方法模式。

如果你需要创建一整套通知平台组件:

  • 邮件平台:发送器、模板解析器、退信处理器;
  • 短信平台:发送器、模板解析器、回执处理器;
  • 企业微信平台:发送器、模板解析器、回调处理器;

这就更适合抽象工厂模式。

chapter 15:抽象工厂模式的优点

1. 保证产品族一致性

这是抽象工厂模式最大的优点。

同一个具体工厂创建出来的对象属于同一个产品族,可以避免混用不兼容的对象。

例如:

  • OSS 上传器搭配 OSS 签名器;
  • MinIO 上传器搭配 MinIO 签名器。

而不是 OSS 上传器搭配 MinIO 签名器。

2. 符合开闭原则的一部分

如果是新增产品族,抽象工厂模式非常友好。

例如现在已经有 OSS 和 MinIO,要新增 COS,只需要新增一组 COS 产品类和一个 COS 工厂类。

已有业务代码可以不用改。

3. 客户端依赖抽象

客户端只依赖:

  • StorageFactory
  • FileUploader
  • FileDownloader
  • UrlSigner

不依赖:

  • OssFileUploader
  • MinioFileDownloader
  • OssUrlSigner

这能降低业务代码和具体技术实现之间的耦合。

4. 适合平台化、插件化扩展

抽象工厂模式非常适合做适配层。

比如:

  • 多云存储;
  • 多支付平台;
  • 多数据库方言;
  • 多消息队列;
  • 多搜索引擎;
  • 多三方服务商 SDK。

chapter 16:抽象工厂模式的缺点

1. 类数量较多

抽象工厂模式会引入很多接口和实现类。

如果产品族里有 5 个产品,平台有 4 个,那么可能就会有 20 个具体产品类,再加上多个工厂类。

这就是典型的“扩展性买单”。

不是坏事,但要看业务是否真的需要。

2. 扩展新的产品等级比较困难

抽象工厂模式适合新增产品族,但不太适合新增产品等级。

例如现在 StorageFactory 只有:

1
2
3
4
5
FileUploader createUploader();

FileDownloader createDownloader();

UrlSigner createUrlSigner();

如果突然要新增一个:

1
FileMetadataReader createMetadataReader();

那么所有具体工厂都要改:

  • OssStorageFactory
  • MinioStorageFactory
  • CosStorageFactory
  • ObsStorageFactory

这就违反了开闭原则。

所以抽象工厂模式的开闭原则是有方向性的:

  • 新增产品族:友好;
  • 新增产品等级:不友好。

3. 抽象层次较多,理解成本更高

对于简单业务来说,抽象工厂可能显得繁琐。

如果你的系统只支持一种存储平台,也没有扩展计划,直接写一个 FileStorageClient 就可以了。

不要为了模式而模式。代码不是孔乙己的长衫,没必要硬穿。

chapter 17:适用场景

抽象工厂模式适合以下场景。

1. 系统需要创建一组相关对象

例如:

  • 一个平台下的一套 SDK 组件;
  • 一个数据库下的一套操作组件;
  • 一个 UI 主题下的一套控件;
  • 一个消息中间件下的一套 Producer、Consumer、AdminClient。

2. 产品族之间不能混用

例如:

  • MySQL SQL 生成器不能搭配 PostgreSQL 方言解析器;
  • OSS 签名器不能搭配 COS 上传器;
  • Windows 风格按钮不应该搭配 Mac 风格输入框。

只要存在“必须成套使用”的约束,抽象工厂就很合适。

3. 需要屏蔽不同平台差异

例如你希望业务层只关心:

1
2
fileUploader.upload(filename, content);
urlSigner.sign(fileKey, expireSeconds);

而不是关心底层到底是 OSS、COS、MinIO 还是 OBS。

4. 平台扩展频繁

如果你现在支持 OSS,未来还要支持 COS、MinIO、OBS、S3,本质上就是不断新增产品族。

这种场景非常适合抽象工厂。

chapter 18:不适合使用的场景

以下场景不建议使用抽象工厂模式。

1. 只创建单个对象

如果只是创建一个对象,比如不同类型的通知发送器,用工厂方法或者简单工厂就够了。

2. 产品之间没有强关联

如果上传器、下载器、签名器之间可以自由组合,并不要求来自同一个平台,那抽象工厂的意义就不大。

3. 产品等级经常变化

如果你的抽象工厂接口经常新增方法,那么所有具体工厂都会被迫修改。

这说明当前抽象不稳定,可能不适合一开始就使用抽象工厂。

4. 业务规模很小

如果只是一个单体小项目,只支持一种实现,没必要上来就抽象出一整套工厂体系。

代码要为变化服务,而不是为“显得专业”服务。

chapter 19:完整案例代码汇总

抽象产品

1
2
3
4
public interface FileUploader {

String upload(String filename, byte[] content);
}
1
2
3
4
public interface FileDownloader {

byte[] download(String fileKey);
}
1
2
3
4
public interface UrlSigner {

String sign(String fileKey, long expireSeconds);
}

OSS 产品族

1
2
3
4
5
6
7
8
public class OssFileUploader implements FileUploader {

@Override
public String upload(String filename, byte[] content) {
System.out.println("使用阿里云 OSS 上传文件:" + filename);
return "oss://" + filename;
}
}
1
2
3
4
5
6
7
8
public class OssFileDownloader implements FileDownloader {

@Override
public byte[] download(String fileKey) {
System.out.println("使用阿里云 OSS 下载文件:" + fileKey);
return new byte[0];
}
}
1
2
3
4
5
6
7
public class OssUrlSigner implements UrlSigner {

@Override
public String sign(String fileKey, long expireSeconds) {
return "https://oss.example.com/" + fileKey + "?expire=" + expireSeconds;
}
}

MinIO 产品族

1
2
3
4
5
6
7
8
public class MinioFileUploader implements FileUploader {

@Override
public String upload(String filename, byte[] content) {
System.out.println("使用 MinIO 上传文件:" + filename);
return "minio://" + filename;
}
}
1
2
3
4
5
6
7
8
public class MinioFileDownloader implements FileDownloader {

@Override
public byte[] download(String fileKey) {
System.out.println("使用 MinIO 下载文件:" + fileKey);
return new byte[0];
}
}
1
2
3
4
5
6
7
public class MinioUrlSigner implements UrlSigner {

@Override
public String sign(String fileKey, long expireSeconds) {
return "https://minio.example.com/" + fileKey + "?expire=" + expireSeconds;
}
}

抽象工厂

1
2
3
4
5
6
7
8
public interface StorageFactory {

FileUploader createUploader();

FileDownloader createDownloader();

UrlSigner createUrlSigner();
}

具体工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class OssStorageFactory implements StorageFactory {

@Override
public FileUploader createUploader() {
return new OssFileUploader();
}

@Override
public FileDownloader createDownloader() {
return new OssFileDownloader();
}

@Override
public UrlSigner createUrlSigner() {
return new OssUrlSigner();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MinioStorageFactory implements StorageFactory {

@Override
public FileUploader createUploader() {
return new MinioFileUploader();
}

@Override
public FileDownloader createDownloader() {
return new MinioFileDownloader();
}

@Override
public UrlSigner createUrlSigner() {
return new MinioUrlSigner();
}
}

业务服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class FileStorageService {

private final StorageFactory storageFactory;

public FileStorageService(StorageFactory storageFactory) {
this.storageFactory = storageFactory;
}

public String uploadAndCreateUrl(String filename, byte[] content) {
FileUploader uploader = storageFactory.createUploader();
UrlSigner urlSigner = storageFactory.createUrlSigner();

String fileKey = uploader.upload(filename, content);

return urlSigner.sign(fileKey, 3600);
}

public byte[] download(String fileKey) {
FileDownloader downloader = storageFactory.createDownloader();

return downloader.download(fileKey);
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
public class Client {

public static void main(String[] args) {
StorageFactory factory = new MinioStorageFactory();

FileStorageService fileStorageService = new FileStorageService(factory);

String url = fileStorageService.uploadAndCreateUrl("design-patterns.md", new byte[0]);

System.out.println("临时访问 URL:" + url);
}
}

chapter 20:一句话总结

抽象工厂模式的本质是:

用一个工厂创建一整套相互关联的产品,保证这些产品属于同一个产品族,并让客户端依赖抽象而不是具体实现。

它适合“多平台、多产品族、成套组件”的场景。

如果只是创建一个对象,用工厂方法。

如果要创建一整套对象,用抽象工厂。

选择设计模式不要看名字高不高级,要看变化点在哪里。变化点找准了,模式才是工具;变化点找错了,模式就是负担。

参考资料

  • Erich Gamma, Richard Helm, Ralph Johnson, Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software.
  • Robert C. Martin. Agile Software Development, Principles, Patterns, and Practices.
  • Joshua Bloch. Effective Java.
  • Spring Framework Documentation: Core Technologies - The IoC Container.
  • Refactoring Guru: Abstract Factory Pattern.
  • SourceMaking: Abstract Factory Design Pattern.

启示录

富贵岂由人,时会高志须酬。

能成功于千载者,必以近察远。


设计模式:抽象工厂模式
https://allendericdalexander.github.io/2026/04/01/java/design/03abstract-factory-pattern-blog/
作者
AtLuoFu
发布于
2026年4月1日
许可协议