欢迎你来读这篇博客,这篇博客主要是关于抽象工厂模式。
其中包括抽象工厂模式的核心思想、适用场景、优缺点、与工厂方法模式的区别,以及一个贴近 Java 后端开发的多云文件存储适配案例。
序言
前面已经聊过简单工厂模式和工厂方法模式。
简单工厂模式解决的是:
把对象创建逻辑集中起来,不让业务代码到处 new 对象。
工厂方法模式解决的是:
把对象创建逻辑交给具体工厂,让新增产品时尽量通过扩展新类完成,而不是修改旧工厂。
那抽象工厂模式解决什么问题?
一句话:
抽象工厂模式解决的是“一组相关对象”的创建问题。
工厂方法通常关注“创建一个产品”。
抽象工厂关注的是“创建一个产品族”。
比如你不是只要一个按钮,而是要一整套 UI 组件:
- Windows 风格按钮;
- Windows 风格输入框;
- Windows 风格弹窗;
或者:
- Mac 风格按钮;
- Mac 风格输入框;
- Mac 风格弹窗。
这一整套风格必须匹配。不能按钮是 Windows 风格,输入框是 Mac 风格,弹窗又是 Linux 风格。那不是系统,是拼多多盲盒。
在 Java 后端开发中,这种场景也很多。例如多云存储适配:
- 阿里云 OSS:上传器、下载器、签名器;
- 腾讯云 COS:上传器、下载器、签名器;
- MinIO:上传器、下载器、签名器。
这些对象通常需要成套使用,这就是抽象工厂模式非常适合的场景。
正文
chapter 1:什么是抽象工厂模式
抽象工厂模式,英文是 Abstract Factory Pattern,属于创建型设计模式,也是 GoF 23 种经典设计模式之一。
它提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
换句话说:
抽象工厂不是只生产一个产品,而是生产一整套产品族。
经典结构一般包括以下角色:
- 抽象工厂:声明创建一组产品的方法。
- 具体工厂:实现抽象工厂,创建某一个产品族中的具体产品。
- 抽象产品:定义某类产品的公共接口。
- 具体产品:实现抽象产品接口,属于某一个具体产品族。
- 客户端:只依赖抽象工厂和抽象产品,不依赖具体产品类。
抽象工厂模式的重点不在于“工厂”两个字,而在于“产品族”。
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:案例背景:多云文件存储适配
下面设计一个多云文件存储模块。
需求如下:
- 系统支持多种文件存储平台;
- 每个平台都提供上传、下载、生成临时访问 URL 的能力;
- 业务层不直接依赖 OSS、COS、MinIO 等具体实现;
- 新增存储平台时,尽量不修改业务代码;
- 同一个请求中使用的上传器、下载器、签名器必须来自同一个平台。
为了简化示例,这里不引入真实 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 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.
启示录
富贵岂由人,时会高志须酬。
能成功于千载者,必以近察远。