✨这里是第七人格的博客✨小七,欢迎您的到来~✨
🍅系列专栏:设计模式🍅
✈️本篇内容: 工厂模式✈️
🍱本篇收录完整代码地址:https://gitee.com/diqirenge/design-pattern 🍱
楔子
记得刚入行的时候,听过一个段子,同样开发一个功能初级程序员要1天,中级程序员要2天,高级程序员要1个星期。当时以为是老油条划水(虽然肯定还是会划一点水,哈哈),但是其实更重要的还是设计思想的不同,经验丰富的程序员往往考虑的更多,不光是业务拓展性,更有程序拓展性。
需求背景
实现一个图片上传功能,逻辑很简单,就是把图片上传到对象存储,但是现在有两个服务商可以选择,他们分别是阿里云和腾讯云,并且现在没有敲定用哪一个,需要你先对接。他们提供的API各不相同,我们假设他们提供的API如下
渠道 | 接口 |
---|---|
阿里云 | void aliUpload(String fileName, String ossId, String token); |
腾讯云 | String tencentUpload(String fileName, String base64, String appID, String appSecret); |
分析设计
因为这2个接口的实现的定义不一样,所以我们最好能抽象出一个统一的接口,让子类去实现自己的业务逻辑,这样的好处是以后腾讯云有变更,就改腾讯云,阿里云有变更就改阿里云,两个子类互不影响(满足单一职责原则)。
又为了不让上层直接使用我们这个统一的方法,所以我们可以再抽象一个类,让他来决定用哪一个上传方式,相当于加了一层防腐,这里我们把他叫做工厂类(创建者与具体的业务解耦)。
再想一下拓展情况,如果以后又加一种上传方式,比如华为云,那我们修改起来也非常方便,只要再加一个子类就可以了,调用方可以不用变(满⾜开闭原则)。
UML图
根据分析设计,我们可以先画一个简单的UML图,后面通过UML图编码
模块名称
factory
模块地址
https://gitee.com/diqirenge/design-pattern/tree/master/src/main/java/com/run2code/design/creational/factory
模块描述
工厂模式代码示例
代码实现
1、首先我们模拟出两个外部接口
上传至oss
/**
* 上传至oss
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/17
*/
public class AliOss {
public void aliUpload(String fileName, String ossId, String token) {
System.out.println("ali upload 入参: " + fileName + " " + ossId + " " + token);
System.out.println("ali 上传成功!");
}
}
上传至cos
/**
* 上传至cos
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/17
*/
public class TencentCos {
public String tencentUpload(String fileName, String base64, String appID, String appSecret) {
System.out.println("tencent upload 入参: " + fileName + " " + base64 + " " + appID + " " + appSecret);
return "成功上传到腾讯云";
}
}
2、然后定义一个用于创建对象的接口或抽象类,让子类决定实例化哪一个类
抽象类
/**
* 抽象的上传操作类,当然他也可以是一个接口
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/15
*/
public abstract class BaseUpDownloader {
/**
* 执行文件上传操作(延迟到子类实现)
*
* @param filePath 文件路径
* @param fileName 文件名称
* @param param 第三方参数
* @return {@link String}
*/
public abstract String doUpload(String filePath, String fileName, Map<String, String> param);
}
上传子类 oss,继承了抽象类,并且调用了上传至oss的方法
/**
* 上传子类 oss
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/15
*/
public class AliOssUpDownloader extends BaseUpDownloader {
private AliOss aliOss;
public AliOssUpDownloader() {
this.aliOss = new AliOss();
}
@Override
public String doUpload(String filePath, String fileName, Map<String, String> param) {
aliOss.aliUpload(fileName, param.get("ossId"), param.get("token"));
return "AliOssUpDownloader upload success";
}
}
上传子类 cos,继承了抽象类,并且调用了上传至oss的方法
/**
* 上传子类 cos,继承了抽象类,并且调用了上传至oss的方法
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/15
*/
public class TencentUpDownloader extends BaseUpDownloader {
private TencentCos tencentCos;
public TencentUpDownloader() {
this.tencentCos = new TencentCos();
}
@Override
public String doUpload(String filePath, String fileName, Map<String, String> param) {
String result = tencentCos.tencentUpload(fileName, param.get("base64"), param.get("appID"), param.get("appSecret"));
System.out.println(result);
return "TencentUpDownloader upload success";
}
}
3、创建一个工厂类,其中包含一个用于创建产品对象的方法。
/**
* 上传的工厂类
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/17
*/
public class UpDownloaderFactory {
/**
* 注册上传下载对象到工厂。
*
* @param upDownloaderName 下载器名称
* @return {@link BaseUpDownloader}
*/
public BaseUpDownloader registerUpDownloader(String upDownloaderName) {
if ("tencent".equalsIgnoreCase(upDownloaderName)) {
return new TencentUpDownloader();
} else if ("ali".equalsIgnoreCase(upDownloaderName)) {
return new AliOssUpDownloader();
}
return null;
}
}
4、编写测试方法
/**
* 测试工厂模式
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/17
*/
public class UpDownloaderFactoryTest {
/**
* 测试调用阿里上传
*/
@Test
public void testAli() {
UpDownloaderFactory factory = new UpDownloaderFactory();
BaseUpDownloader upDownloader = factory.registerUpDownloader("ali");
Map<String, String> param = new HashMap<>();
param.put("ossId", "ossId");
param.put("token", "token");
if (upDownloader == null) {
return;
}
System.out.println("==========上传开始==========");
String result = upDownloader.doUpload("/temp/file", "阿里文件.pdf", param);
System.out.println("结果:" + result);
System.out.println("==========上传结束==========");
}
/**
* 测试调用腾讯上传
*/
@Test
public void testTencent() {
UpDownloaderFactory factory = new UpDownloaderFactory();
BaseUpDownloader upDownloader = factory.registerUpDownloader("tencent");
Map<String, String> param = new HashMap<>();
param.put("base64", "base64");
param.put("appID", "appID");
param.put("appSecret", "appSecret");
if (upDownloader == null) {
return;
}
System.out.println("==========上传开始==========");
String result = upDownloader.doUpload("/temp/file", "腾讯文件.pdf", param);
System.out.println("结果:" + result);
System.out.println("==========上传结束==========");
}
}
5、测试结果
①执行testAli方法
==========上传开始==========
ali upload 入参: 阿里文件.pdf ossId token
ali 上传成功!
结果:AliOssUpDownloader upload success
==========上传结束==========
②执行testTencent方法
==========上传开始==========
tencent upload 入参: 腾讯文件.pdf base64 appID appSecret
成功上传到腾讯云
结果:TencentUpDownloader upload success
==========上传结束==========
实现要点
定义一个用于创建对象的接口或抽象类,让子类决定实例化哪一个类。
示例中为:BaseUpDownloader
创建一个工厂类,其中包含一个用于创建产品对象的方法。
示例中,工厂类为:UpDownloaderFactory;创建产品对象的方法为:registerUpDownloader
在客户端代码中,通过调用工厂类的方法来创建所需的产品对象,而无需直接调用具体的产品类的构造函数。
示例中为:
UpDownloaderFactory factory = new UpDownloaderFactory();
BaseUpDownloader upDownloader = factory.registerUpDownloader("ali");
总结
本文从模拟需求开始,带着读者一起学习了工厂模式,从上文可知工厂模式其实非常简单,掌握三大实现要点就可以了。所以小七在工作中使用工厂模式的频率也非常的高,但是工厂模式一般是不会单独使用的,他的好伙伴有策略模式、单例模式、模版模式等等,后面小七都会讲到。
本文由博客一文多发平台 OpenWrite 发布!