✨这里是第七人格的博客✨小七,欢迎您的到来~✨
🍅系列专栏:设计模式🍅
✈️本篇内容: 工厂模式✈️
🍱本篇收录完整代码地址: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();}@Overridepublic 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();}@Overridepublic 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 {/*** 测试调用阿里上传*/@Testpublic 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("==========上传结束==========");}/*** 测试调用腾讯上传*/@Testpublic 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 发布!