把「策略模式」应用到实际项目中

阅读原文:把「策略模式」应用到实际项目中java

不管你知不知道这个设计模式,但一定在项目中都似曾相识。假若仅仅聊理论必然枯燥乏味,只有理论和实战相结合方可达到人剑合一的境界。面试

首先,我来讲个需求,假若是你遇到该如何作?你可停留几分钟,想出你的解决方式,可在下方留言,说出你的想法。数据库

需求

用户有文件上传的需求,而咱们要负责对文件进行存储,因为咱们的系统可能会单独给个别客户私有化部署(部署尽可能少依赖中间件能服务等),同时咱们也会本身运营成为本身的SaaS服务(保证服务的高可用等)。设计模式

因此咱们有两点需求:微信

  1. 要在SaaS版本中将文件存入分布式存储系统fastDfs中架构

  2. 客户的私有部署中将文件​存储在数据库中框架

思路

寻找异同点

  1. 文件上传过程这个不管什么版本部署都是同样的,因此暂不考虑。分布式

  2. 文件存储的方式不一样,同时文件的获取和删除也不一样ide

  3. 保存,获取,删除后的响应也是相同的,也不考虑了。spa

开始抽象

当前咱们考虑的就是文件的存储,获取和删除了。一样的行为,不一样的实现,咱们一定想到定义一个接口:

/** * 文件存储接口 * Identify表示文件的惟一标识,可任意类型 * T 表示 上传下载的返回类型,可任意类型 * * @author flyhero * @date 2019-02-01 11:18 AM */
public interface IStorageService<Identify, T> {

    /** * 上传文件 * * @param file * @return */
    T upload(MultipartFile file);

    /** * 下载文件 * * @param identify * @return */
    T download(Identify identify);

    /** * 删除文件 * * @param identify */
    void delete(Identify identify);
}
复制代码

####两种不一样的实现

  • 存储FastDfs
@Slfj
@Service("fastDfsServiceImpl")
public class FastDfsServiceImpl implements IStorageService<String,FileVo> {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVo upload(MultipartFile multipartFile){
        logger.info("存储在fastDfs……");
    }

    @Override
    public FileVo download(String hash) {
        logger.info("从fastDfs下载文件");
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String hash) {
        logger.info("从fastDfs删除文件");
    }
}

复制代码
  • 存储 数据库
@Slfj
@Service("databaseServiceImpl")
public class DatabaseServiceImpl implements IStorageService<String,FileVo> {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVo upload(MultipartFile file) {
        logger.info("存储在database……");
        return null;
    }

    @Override
    public FileVo download(String hash) {
        logger.info("从database下载文件");
        return null;
    }

    @Override
    public void delete(String hash) {
        logger.info("从database删除文件");
    }
}
复制代码
  • 调用
@Service
public class FileServiceImpl implements FileService {

// 同一个接口根据不一样的名称调用不一样的实现
// @Qualifier("databaseServiceImpl")
    @Qualifier("fastDfsServiceImpl")
    @Autowired
    private IStorageService storageService;
  
    public void save(MultipartFile file){
        if (null == file) {
            throws new Exception("文件不能为空");
        }
        FileVo fileVo = storageService.upload(file);
    }
}
复制代码

疑惑

可能有人会说了:这怎么和我了解的策略模式不同啊,策略模式不是这样的吗?

你说的对!但这是在没有任何框架下的设计模式,而咱们如今广泛使用的都是Spring框架,那么标准的策略模式中的Context去哪里了?

  • 引入Context的做用

首先咱们要知道引入Context的做用,是为了不高层直接与策略接口直接交互,为何呢?由于咱们策略模式接口功能相对比较单一,而有些高层模块可能须要一些比较复杂的交互。

  1. 若直接调用接口,则须要对每一个实现增长逻辑;
  2. 若直接调用前,执行加强逻辑,那么多个地方使用时,会存在重复加强逻辑,并可能忘掉。

此时引入Context是解决问题的最佳方式。

而咱们的FileServiceImpl就至关于Context的做用,因为咱们使用Spring框架且使用了三层架构,暴露上传文件、下载文件、删除文件是在controller层三个不一样的方法中(或不一样的controller中),为了不几个地方使用的存储策略不一样,我直接在Context中来指定使用的策略,当须要切换时也很是方便,只须要更改IStorageService的注解便可。

总结

优势

  • 策略实现类可自由切换
  • 易于扩展,若还有新的策略只要新增策略接口一个实现类便可
  • 没必要使用条件语句进行决定使用哪一个策略

缺点

  • 策略类一旦增多,调用者要清楚的知道各类策略的区别

若是你有不一样的见解,还望不吝指教。

发现这个面试视频不错,分享给你们,公众号回复: 面试视频 和 架构师

20190424123323.jpg

更多精彩技术文章尽在微信公众号:码上实战

相关文章
相关标签/搜索