设计模式Java实现-工厂模式

964 阅读6分钟

✨这里是第七人格的博客✨小七,欢迎您的到来~✨

🍅系列专栏:设计模式🍅

✈️本篇内容: 工厂模式✈️

🍱本篇收录完整代码地址:gitee.com/diqirenge/d… 🍱

楔子

记得刚入行的时候,听过一个段子,同样开发一个功能初级程序员要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图编码

工厂模式UML.png

模块名称

factory

模块地址

gitee.com/diqirenge/d…

模块描述

工厂模式代码示例

代码实现

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

==========上传结束==========

实现要点

  1. 定义一个用于创建对象的接口或抽象类,让子类决定实例化哪一个类。

    示例中为:BaseUpDownloader

  2. 创建一个工厂类,其中包含一个用于创建产品对象的方法。

    示例中,工厂类为:UpDownloaderFactory;创建产品对象的方法为:registerUpDownloader

  3. 在客户端代码中,通过调用工厂类的方法来创建所需的产品对象,而无需直接调用具体的产品类的构造函数。

    示例中为:

    UpDownloaderFactory factory = new UpDownloaderFactory();

    BaseUpDownloader upDownloader = factory.registerUpDownloader("ali");

总结

本文从模拟需求开始,带着读者一起学习了工厂模式,从上文可知工厂模式其实非常简单,掌握三大实现要点就可以了。所以小七在工作中使用工厂模式的频率也非常的高,但是工厂模式一般是不会单独使用的,他的好伙伴有策略模式、单例模式、模版模式等等,后面小七都会讲到。