【原创实现 设计模式】Spring+策略+模版+工厂模式去掉if-else,实现开闭原则,优雅扩展

发布于:2024-07-04 ⋅ 阅读:(16) ⋅ 点赞:(0)

1 定义与优点

1.1 定义

策略模式(Strategy Pattern)属于对象的⾏为模式。他主要是用于针对同一个抽象行为,在程序运行时根据客户端不同的参数或者上下文,动态的选择不同的具体实现方式,即类的行为可以在运行时更改。
策略模式定义了一系列算法或实现策略,并将每个算法封装在独立的类中,所以对一个策略行为进行修改、添加或者删除策略时不会影响到原有的策略,也就是开闭原则的具体表现。

1.2 优势

  • 方便扩展:使用策略模式可以方便的添加、删除、替换策略,只需要增加策略类即可,不需要修改原有代码
  • 可读性好:不通的策略实现分布在不通的实现类中互不依赖,结构清晰,易于理解。
  • 避免大量的条件判断:使用传统的if-else在分支过多时难以维护,并且不符合开闭原则,增加策略类型时需要增加if-else代码,使用策略模式即可避免这一点。

2 场景与目标

假如你的项目对于文件上传这个业务支持多种具体实现,不仅支持AWS S3、腾讯云 COS、阿里云 OSS,后续还可能接入华为云、Oracle云等云厂商的对象存储。每个待上传的文件都有一个需要上传到哪个对象存储类型的属性,那么对于文件上传有以下的处理步骤:

  • 根据待上传文件信息选择选择不通的对象存储的上传客户端
  • 一些公共的逻辑,比如参数校验,日志记录
    在这里插入图片描述

3 实现

可以使用策略模式+模版方法+工厂模式,并在Spring中进行应用实现。

  • 定义一个文件上传的策略接口
  • 定义一个抽象类实现这个接口,并实现不同对象存储的通用逻辑,定义子类的模版方法
  • 具体不同的对象存储客户端实现抽象类,并实现抽象方法并且定义为一个bean
  • 定义一个工厂管理具体的实现类对象
  • 客户端使用工厂类来获取不同对象存储的具体实现类

具体实现如下:

  1. 定义文件上传的策略接口
public interface CloudStorage {

    /**
     * 上传文件
     *
     * @param uploadPath 上传文件到哪个目录下
     * @param localFile 待上传的文件
     */
    void upload(String uploadPath, File localFile);

}
  1. 定义一个公共的抽象类,所有对象存储都会用到的通用处理逻辑放在这里,然后定义一个doUpload()方法让子类来实现,也即模版方法的具体体现
@Slf4j
public abstract class AbstractCloudStorage implements CloudStorage {

    /**
     * 上传文件
     *
     * @param uploadPath 上传文件到哪个目录下
     * @param localFile  待上传的文件
     */
    @Override
    public void upload(String uploadPath, File localFile) {
        if (StringUtils.isBlank(uploadPath) || Objects.isNull(localFile) || !localFile.exists()) {
            log.warn("参数异常, uploadPath={}, localFile={}", uploadPath, localFile.getPath());
            return;
        }
        long start = System.currentTimeMillis();
        doUpload(uploadPath, localFile);
        log.info("上传完成,耗时:{}ms", (System.currentTimeMillis() - start));
    }

    /**
     * 上传文件
     *
     * @param uploadPath 上传文件到哪个目录下
     * @param localFile  待上传的文件
     */
    public abstract void doUpload(String uploadPath, File localFile);
}
  1. 定义不同策略的实现类,增加一种策略只需要增加一个类即可,以达到开放封闭的目的
@Slf4j
@Component
public class CosClient extends AbstractCloudStorage {

    /**
     * 上传文件
     *
     * @param uploadPath 上传文件到哪个目录下
     * @param localFile 待上传的文件
     */
    @Override
    public void doUpload(String uploadPath, File localFile) {
        log.info("Tencent COS 上传文件");
    }
}

@Slf4j
@Component
public class S3Client extends AbstractCloudStorage {

    /**
     * 上传文件
     *
     * @param uploadPath 上传文件到哪个目录下
     * @param localFile 待上传的文件
     */
    @Override
    public void doUpload(String uploadPath, File localFile) {
        log.info("AWS S3 上传文件");
    }
}
  1. 最后定义一个工厂类,来获取不同类型的对象存储策略的文件上传实例
@Slf4j
@Component
public class CloudStorageClientFactory {

    @Autowired
    private Map<String, CloudStorage> cloudStorageMap;

    /**
     * 通过对象存储类型获取实际客户端
     *
     * @param storageType 对象存储类型
     * @return 对象存储客户端
     */
    public CloudStorage getByType(String storageType) {
        return cloudStorageMap.get(storageType);
    }
}

4 客户端调用

通过上面的对策略模式的定义和实现,接下来创建一个上传文件的服务来处理文件上传请求

@Slf4j
@Component
public class FileUploadService {

    @Autowired
    private CloudStorageClientFactory cloudStorageClientFactory;
    
    /**
     * 文件上传
     */
    public void fileUpload(FileInfo fileInfo) {
        // 通过文件需要上传的存储类型获取对应的客户端
        CloudStorage cloudStorage = cloudStorageClientFactory.getByType(fileInfo.getStorageType());
        // 执行客户端的文件上传
        cloudStorage.upload(fileInfo.getUploadPath(), fileInfo.getFile());
    }
}

通过以上的实现,使用了工厂模式来创建不同类型的对象存储客户端实例,使用策略模式来处理文件上传请求来避免了if-else条件判断,代码简洁易于维护和扩展。
使用模版方法处理了上传文件的公共逻辑,实现了代码优雅复用。这样的实现方式在项目中的实际使用非常频繁。