背景

最近在忙一个需求,大致就是给满足特定条件的用户发营销邮件,但是用户的来源有很多方式:从 ES 查询的、从 csv 导入的、从 MongoDB 查询….. 需求很简单,但是怎么写的优雅,方便后续扩展,就存在很多门道了。

我们的项目是基于 Spring Boot 开发的,因此这篇文章也会基于 Spring Boot 作为基础框架,教你如何使用 Spring 依赖注入的特性,优雅的实现策略模式。

1. 简单粗暴

最简单粗暴直接的方式莫过于 if...else… 了,伪代码如下:

if(来源 == ES){

// TODO: ES Query

}else if(来源 == CSV){

// TODO: Read CSV File

}else if(来源 == MongoDB){

// TODO: MongoDB Query

}

如果后面还需要从其他平台获取,那就在接着添加 else if...,这种方式固然简单直接,但是当后续扩展的方式越来越多,相应的if...else...也会越来越长,emmm….. 怎么说呢,黑猫白猫,能抓到老鼠的就是好猫。

2. 策略模式

在 Spring 环境下实现策略模式异常简单,毕竟 Spring 提供的依赖注入简直就是开发利器~

既然是策略模式,那么定义策略肯定是首当其冲,策略我们使用枚举实现最佳。

public enum GroupType {

/**

* 从 ES 查询

*/

ES,

/**

* 从 MongoDB 查询

*/

MONGODB,

/**

* 从 文件 读取

*/

FILE

}

下一步,我们定义一个接口,用于抽象通用的功能。

public interface IGroupSelect {

/**

* 人群获取方式

*

* @return 人群选择枚举

*/

GroupType type();

/**

* 查询满足条件的用户

*

* @param groupQuery 查询条件

* @return 满足条件的用户

*/

default List queryUser(GroupQuery groupQuery) {

checkQueryCondition(groupQuery);

return doQuery(groupQuery);

}

/**

* 事前校验查询条件

*

* @param groupQuery 查询条件

* @throws IllegalArgumentException 参数异常

*/

void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException;

/**

* 真正的查询方法

*

* @param groupQuery 查询条件

* @return 满足条件的用户

*/

List doQuery(GroupQuery groupQuery);

}

这一步,小伙伴们有没有发现里面也包含了模板方法模式呢?

然后就是不同策略的具体实现了。

ES 策略

@Slf4j

@Service

public class EsGroupSelect implements IGroupSelect {

/**

* 人群获取方式

*

* @return 人群选择枚举

*/

@Override

public GroupType type() {

return GroupType.ES;

}

/**

* 事前校验查询条件

*

* @param groupQuery 查询条件

* @throws IllegalArgumentException 参数异常

*/

@Override

public void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException {

log.info("groupQuery = {}", groupQuery);

}

/**

* 查询满足条件的用户

*

* @param groupQuery 查询条件

* @return 满足条件的用户

*/

@Override

public List doQuery(GroupQuery groupQuery) {

List result = new ArrayList<>();

// TODO:

// 1. 复杂的 ES 查询逻辑

// 2. 根据条件筛选满足条件的用户数据

for (int i = 1; i <= 15; i++) {

result.add(GroupUser.of("ES用户" + i, i + "@es.com"));

}

return result;

}

}

文件策略

@Slf4j

@Service

public class FileGroupSelect implements IGroupSelect {

/**

* 人群获取方式

*

* @return 人群选择枚举

*/

@Override

public GroupType type() {

return GroupType.FILE;

}

/**

* 事前校验查询条件

*

* @param groupQuery 查询条件

* @throws IllegalArgumentException 参数异常

*/

@Override

public void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException {

log.info("groupQuery = {}", groupQuery);

}

/**

* 查询满足条件的用户

*

* @param groupQuery 查询条件

* @return 满足条件的用户

*/

@Override

public List doQuery(GroupQuery groupQuery) {

List result = new ArrayList<>();

// TODO:

// 1. 复杂的解析、读文件

// 2. 根据条件筛选满足条件的用户数据

for (int i = 1; i <= 3; i++) {

result.add(GroupUser.of("文件读取用户" + i, i + "@file.com"));

}

return result;

}

}

MongoDB 策略

@Slf4j

@Service

public class MongoGroupSelect implements IGroupSelect {

/**

* 人群获取方式

*

* @return 人群选择枚举

*/

@Override

public GroupType type() {

return GroupType.MONGODB;

}

/**

* 事前校验查询条件

*

* @param groupQuery 查询条件

* @throws IllegalArgumentException 参数异常

*/

@Override

public void checkQueryCondition(GroupQuery groupQuery) throws IllegalArgumentException {

log.info("groupQuery = {}", groupQuery);

}

/**

* 查询满足条件的用户

*

* @param groupQuery 查询条件

* @return 满足条件的用户

*/

@Override

public List doQuery(GroupQuery groupQuery) {

List result = new ArrayList<>();

// TODO:

// 1. 复杂的 MongoDB 查询逻辑

// 2. 根据条件筛选满足条件的用户数据

for (int i = 1; i <= 7; i++) {

result.add(GroupUser.of("MongoDB用户" + i, i + "@mongo.com"));

}

return result;

}

}

现在到了最后一步,就是如何通过 Spring 优雅的实现策略模式的选择呢?敲黑板,考试必考!

我们通过定义一个工厂类,然后使用 Spring 的依赖注入特性,可以注入一个接口的多个实现,这里采用 List 的形式注入,Spring 也支持通过 Map 的形式注入,如果使用 Map 注入,那么 key 就是类名,小伙伴们自己也可以测试一下~

@Service

public class GroupSelectFactory {

@Autowired

private List groupSelectList;

/**

* 根据人群类型选择具体的实现类

*

* @param type 人群类型

* @return 人群选择具体实现类

*/

public IGroupSelect getGroupSelect(GroupType type) {

Optional groupSelectOptional = groupSelectList.stream().filter(t -> t.type() == type).findAny();

return groupSelectOptional.orElseThrow(() -> new IllegalArgumentException("暂不支持该人群方式"));

}

}

最后写个定时任务测试一下吧。

@Autowired

private GroupSelectFactory groupSelectFactory;

/**

* 模拟定时发送营销邮件

*/

@Scheduled(cron = "0/10 * * * * ?")

public void sendEmailTask() {

List taskList = new ArrayList<>();

for (GroupType groupType : GroupType.values()) {

GroupQuery groupQuery = new GroupQuery("虚头巴脑的 " + groupType.name() + " 查询条件");

taskList.add(SendEmailTask.of(groupType, groupQuery));

}

taskList.forEach(task -> {

List groupUsers = groupSelectFactory.getGroupSelect(task.getType()).queryUser(task.getQuery());

log.info("groupUsers = {}", groupUsers);

});

}

@Data

@NoArgsConstructor

@AllArgsConstructor(staticName = "of")

static class SendEmailTask implements Serializable {

private static final long serialVersionUID = -3461263089669779193L;

private GroupType type;

private GroupQuery query;

}

观察控制台,看看日志输出吧~

总结

本文使用策略模式实现不同人群的查询,后续如果要增加短信、微信、钉钉的消息发送,是不是也可以用策略模式实现呢?

使用 Spring 的依赖注入特性,可以注入一个接口的多个实现,很容易就实现了策略模式的选择,这样后续添加一种策略的时候,完全不需要改动主要逻辑,只需添加具体实现即可。

细心的小伙伴可以发现,本文虽然是讲策略模式,其实里面还包含了模板方法、工厂模式,多种设计模式的协同作战,食用味道更佳哟~

以上就是Spring 环境下实现策略模式的示例的详细内容,更多关于Spring 实现策略模式的资料请关注脚本之家其它相关文章!

java spring 实现策略,Spring 环境下实现策略模式的示例相关推荐

  1. Linux环境下基于策略的路由

    Linux环境下基于策略的路由 原文作者:Matthew G. Marsh 原文出处:[url]http://www.sysadminmag.com/linux/articles/v09/i01/a3 ...

  2. java反序列化漏洞POP查找_利用 Java 反序列化漏洞在受限环境下获取反向 Shell

    原标题:利用 Java 反序列化漏洞在受限环境下获取反向 Shell 原文链接: https://medium.com/abn-amro-red-team/java-deserialization-f ...

  3. 供应链环境下VMI运作模式研究

    内容摘要:随着供应链管理的发展,供应商库存管理作为一种先进库存管理理念逐渐流行起来.文章根据核心企业在供应链中的不同位置提出了VMI运行结构,并按照不同的运行结构分析了企业可以采用的运行模式,以及各种 ...

  4. PaddleOCR——Docker环境下基于HubServing模式部署Restful API服务(CPU版本)

    Docker环境下基于HubServing模式部署Restful API服务(CPU版本) 在日常项目应用中,相信大家一般都会希望能通过Docker技术,把PaddleOCR服务打包成一个镜像,以便在 ...

  5. java web shiro_javase和javaweb环境下shiro的搭建

    shiro-1 javase环境下搭建shiro 1.导入jar包 2.配置文件:存储临时文件 shiro.ini文件:存储数据,用户名,密码,角色,权限 3.代码 // 1.获取安全管理器 Fact ...

  6. java中HashMap在多线程环境下引起CPU100%的问题解决

    最近项目中出现了Tomcat占用CPU100%的情况,原以为是代码中出现死循环,后台使用jstack做了dump,发现是系统中不合理使用HashMap导致出现了死循环(注意不是死锁). 产生这个死循环 ...

  7. redis持久化策略梳理及主从环境下的策略调整记录

    转载自 https://www.cnblogs.com/kevingrace/p/6266319.html redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到磁盘来 ...

  8. java本地可以发到linux不行,java 使用 ftp 在windows环境下可以正常下载文件,在linux环境下不行...

    windows环境下使用 public static void dowmloadFtpWindos(String fileUuid, HttpServletRequest request,HttpSe ...

  9. java socket tomcat_在Tomcat环境下使用socket通信

    最近在做一个APP的服务器端,但是APP和服务器端使用的是HTTP的通信协议,而另一方与服务器端通信却使用的是自定义的通信协议.具体的系统拓扑如下: 为了完成以上的需求,一般的解决方案有两种: 自己实 ...

最新文章

  1. 项目管理:多项目同时进行如何做好进度管理?
  2. java jar包搜索地址 and ADB 连接模拟器
  3. Python对函数求偏导数
  4. laravel安装笔记
  5. HTML里Dom onload和jQuery document ready这两个事件的区别
  6. 第十八期:专家认为对“人工智能+教育”应持审慎态度
  7. [弹性力学]弹力与材力的同一变量的规定正方向的不同导致公式中个别符号不同
  8. 如何设置STM8单片机选项字
  9. 名企面试官精讲典型编程题之C++篇
  10. Intent 与 IntentFilter 详解
  11. 使用visio 2010建立sql server数据模型——手动画、利用逆向工程
  12. [九度][何海涛] 最小的K个数
  13. 戴尔t40服务器接显示器,Dell PowerEdge T40 机架式服务器
  14. C语言大小端转换问题
  15. php网站信息更新时间,php显示当前时间_php 自动更新时间
  16. 深入了解示波器(五):实时采样与等效时间采样
  17. Eclipse中配置python环境
  18. error: OpenCV(4.1.2) ..\modules\imgcodecs\src\loadsave.cpp:715: error: (-215:Assertion failed) !_img
  19. [转载]ZT:趋势交易者的利器----MACD指标在美元指数判断与实战中的运用
  20. linux磁盘上机题目,Linux 练习题-3文件与磁盘 问答

热门文章

  1. 24日直播预告丨你们的“落落大神”来分享数据模型重构案例啦!
  2. 本周四直播:Oracle 19c 升级实战分享
  3. 【演讲实录】RWP团队谈SQL优化
  4. IoT平台如何实现业务配置中心
  5. Volcano:在离线作业混部管理平台,实现智能资源管理和作业调度
  6. 带你掌握Vue过滤器filters及时间戳转换
  7. 循环神经网络LSTM RNN回归:sin曲线预测
  8. 毕昇 JDK:为啥是ARM 上超好用的 JDK
  9. 从中心到边缘,IoT正变为互联网之上的一张大网
  10. 快速了解前端开发HTML的正确姿势