使用背景
最近项目中使用到了策略模式。
本来硕是想用拉姆达表达式来实现。
但是怕自己菜的自己都不知道自己写的是啥。
于是就不用拉姆达表达式来实现策略模式了。
我还是先润一润如何面向对象吧
需求背景
我的任务项目任务中呢,都存在RPC调用,但是呢对于响应的返回值都有不同的操作。所以我首先就想到了策略模式。
灵魂三问:如何实现策略模式呢?什么时候使用策略模式呢?策略模式有什么优缺点?
设计思路
第一步,分析需求。我一共有三个任务。具体是就是类似这样
任务一:调用接口A拿到响应结果,利用响应结果存表。
任务二:扫表,拿到关键字段c1做参数,调用接口B,利用响应结果存表。
任务三:扫表,拿到关键字段c2做参数,调用接口C,利用响应结果存表。
很显然看出这三个任务共同特征,都是调用接口,处理响应结果。只不过是调用的接口不一样,处理不同的响应结果。
好! 那这就是策略模式的关键
多种任务存在同一个动作或行为时,就可以通过策略模式来解决。
举个几个例子:
动作:转账,任务:支付宝转账,微信转账,银行卡转账。
动作:跑步,任务:快跑,狂奔,裸奔。
动作:炫饭,任务:养生炫,狂炫,厨子不噶肉还往死里炫。
总结
像这样都是将动作设计为:策略接口
将任务设计为:不同策略
然后在策略执行者对外暴露API,来自定义调用策略(根据你自己的业务而定)可以理解成执行器就是来执行这一动作的。例如 动作为:转账,那么执行器就执行转账(这一个操作很像是一个代理增强操作)
最后在调用执行器执行动作时选择执行策略即可,即选择支付宝转账或者微信转账或者银行卡转账。
策略模式
优点:
1.定义了一系列的算法来实现策略规范功能,并将算法封装起来,提高安全性。
2.策略之间可以替换,某个算法的调整或者新增不会影响其他的算法。
3.是在执行期间来选择具体的算法,使用什么算法被推迟到运行时。
4.使得系统更加灵活,复用性更高,该模式可以减少if else的数量,属于行为型。
缺点:
1.码农必须知道所有策略是干嘛的
2.策略实现类会变多,不易维护。
代码实例
目的:根据任务的共同特征,来实现不同策略
Tip:下面的RPC调用接口我就不贴出来了。所以我返回的是null。
第一步:定义策略接口
/**
* Author: thelongi
* Date: 2022/4/21
* Describe: 任务调用Api策略
*/
public interface TaskApiStrategy {
/**
* 接收调用接口
*/
default JSONObject receive(JSONObject jsonObject){
//调用接口
return null;
}
/**
* 处理接口结果
*/
default JSONObject handle(JSONObject jsonObject){
//返回接口响应信息
return jsonObject;
}
}
第二步:定义策略执行者
这些策略肯定是要去调用的,那么如何调用呢?
/**
* Author: thelongi
* Date: 2022/4/21
* Describe: 任务API执行器
*/
public class TaskApiExecutor {
TaskApiStrategy taskApiStrategy;
public TaskApiExecutor(TaskApiStrategy taskApiStrategy){
this.taskApiStrategy = taskApiStrategy;
}
/**
* @param jsonObject 请求参数
* @return 处理接口的结果
*/
@Transactional
public JSONObject execute(JSONObject jsonObject){
//接收数据接口
JSONObject receive = this.taskApiStrategy.receive(jsonObject);
//处理数据接口
return this.taskApiStrategy.handle(receive);
}
}
第三步:策略实现
/**
* Author: thelongi
* Date: 2022/4/21
* Describe: 合格证下载信息策略
*/
@Component
public class DownCertificateInfoStrategy implements TaskApiStrategy {
@Autowired
BizDtQueueDownCertificateDao bizDtQueueDownCertificateDao;
@Autowired
BizDtCertificateDao bizDtCertificateDao;
@Autowired
BizDtQueueDownNoticeDao bizDtQueueDownNoticeDao;
/**
* 接收合格证下载信息
*
* @param jsonObject 包含 dph 底盘号 factoryCode 工厂代码
* @return json格式的合格证清单队列数据
*/
@Override
public JSONObject receive(JSONObject jsonObject) {
Log4jUtil.info("接收合格证下载信息接口 \n请求参数:" + jsonObject.toJSONString());
//调用查询底盘号接口
return null;
}
/**
* 处理合格证信息下载
*
* @param jsonObject 包含 dph 底盘号 factoryCode 工厂代码
* @return 响应状态
*/
@Override
public JSONObject handle(JSONObject jsonObject) {
if (jsonObject == null) {
return null;
}
Log4jUtil.info("处理合格证下载信息接口开始");
//获取status
String status = jsonObject.getString("status");
//处理失败
if (StringUtils.equals(status, MsgConstantType.STATUS_FAIL)) {
Log4jUtil.error("调用目标接口返回状态为0====>处理失败");
}
//处理成功
else if (StringUtils.equals(status, MsgConstantType.STATUS_SUCCESS)) {
Log4jUtil.info("调用目标接口返回状态为1====>开始处理");
//获取合格证信息
JSONObject hgzxx = jsonObject.getJSONObject("hgzxx");
//json格式转换成 List集合
List<BizDtCertificate> hgzxxList = JSON.parseArray(hgzxx.toJSONString(), BizDtCertificate.class);
//处理合格证信息
int rows = this.handleList(hgzxxList);
Log4jUtil.info("影响的行数:" + rows);
}
Log4jUtil.info("处理合格证下载信息接口结束");
//返回接口响应结果
return jsonObject;
}
/**
* 处理合格证信息结果集
*
* @param list 操作行
* @return 影响行
*/
private int handleList(List<BizDtCertificate> list) {
//定义影响行数
int rows = 0;
//定义影响行数
for (BizDtCertificate e : list) {
try {
//根据主键查找某一行
if (bizDtCertificateDao.selectOneByKey(e) == null) {
//不存在,则插入
rows += bizDtCertificateDao.insertByEntity(e);
//处理合格证信息,存入公告队列
loadToQueue(e);
}
} catch (Exception err) {
Log4jUtil.error("插入下载公告队列异常---vin:" + e.getVnm(),err);
}
}
return rows;
}
/**
* 存入基础公告数据下载队列
*/
private void loadToQueue(BizDtCertificate bizDtCertificate) {
//封装公告下载实体
BizDtQueueDownNotice e = new BizDtQueueDownNotice();
e.setVin(bizDtCertificate.getVnm());
e.setXrsj(new Date());
//设置待下载标记
e.setXzbj(MsgConstantType.DOWN_DXZ);
//存表
Integer rows = bizDtQueueDownNoticeDao.insertByEntity(e);
Log4jUtil.info("存入基础公告数据下载队列 影响行"+rows);
}
}
/**
* Author: thelongi
* Date: 2022/4/21
* Describe: 接收合格证变更清单API策略
*/
@Component
public class DownCertificateChangeStrategy implements TaskApiStrategy {
@Autowired
BizDtQueueDownCertificateDao bizDtQueueDownCertificateDao;
/**
* 接收合格证变更清单接口
*
* @param jsonObject 包含 startTime 起始时间 截止时间 factoryCode 工厂代码
* @return json格式的合格证清单队列数据
*/
@Override
public JSONObject receive(JSONObject jsonObject) {
Log4jUtil.info("接收合格证变更清单接口 \n请求参数:" + jsonObject.toJSONString());
//调用接口 todo 还需完成调接口的操作
return null;
}
/**
* 处理合格证变更清单接口
*
* @param jsonObject 合格证
* @return 处理成功返回1 失败返回0
*/
@Override
public JSONObject handle(JSONObject jsonObject) {
if (jsonObject == null) {
return null;
}
Log4jUtil.info("处理合格证变更清单接口开始");
//获取status
String status = jsonObject.getString("status");
//接口响应成功
if (StringUtils.equals(status, MsgConstantType.STATUS_SUCCESS)) {
Log4jUtil.info("调用目标接口返回状态为1====>开始处理");
//获取合格证信息
JSONObject hgzxx = jsonObject.getJSONObject("hgzxx");
//获取到的合格证信息
List<BizDtQueueDownCertificate> list = JSON.parseArray(hgzxx.toJSONString(), BizDtQueueDownCertificate.class);
//处理合格证信息
this.handleList(list);
}
Log4jUtil.info("处理合格证变更清单接口结束");
return jsonObject;
}
/**
* 处理合格证下载队列结果集
*
* @param list 操作行
*/
private void handleList(List<BizDtQueueDownCertificate> list) {
//定义影响行数
int rows = 0;
//定义影响行数
for (BizDtQueueDownCertificate e : list) {
try {
//根据主键查找某一行
if (bizDtQueueDownCertificateDao.selectOneByKey(e) == null) {
//不存在,则插入
//设置写入时间
e.setXrsj(new Date());
//设置待下载标记
e.setXzbj(MsgConstantType.DOWN_DXZ);
rows += bizDtQueueDownCertificateDao.insertByEntity(e);
}
} catch (Exception err) {
Log4jUtil.error("插入合格证下载队列异常---车辆识别号为:" + e.getVin(),err);
}
}
Log4jUtil.info("影响的行数:" + rows);
}
}
/**
* Author: thelongi
* Date: 2022/4/21
* Describe: 基础公告数据API策略
*/
@Component
public class DownBasicNoticeDataStrategy implements TaskApiStrategy {
@Autowired
BizDtNoticeDao bizDtNoticeDao;
@Autowired
BizDtQueueDownNoticeDao bizDtQueueDownNoticeDao;
@Autowired
BizDtQueuePushCertificateDao bizDtQueuePushCertificateDao;
/**
* 接收基础公告数据接口
*
* @param jsonObject 包含 cpno 产品公告号 factoryCode 工厂代码
* @return json格式的合格证清单队列数据LGJE5FE00 GM532027
*/
@Override
public JSONObject receive(JSONObject jsonObject) {
Log4jUtil.info("接收基础公告数据接口 \n请求参数:"+jsonObject.toJSONString());
//调用接口
return null;
}
/**
* 处理基础公告数据
*
* @param jsonObject 接口响应结果
* @return json格式的合格证清单队列数据
*/
@Override
public JSONObject handle(JSONObject jsonObject) {
if(jsonObject == null){
return null;
}
Log4jUtil.info("处理基础公告数据接口开始");
//获取status
String status = jsonObject.getString("status");
//处理失败
if (StringUtils.equals(status, MsgConstantType.STATUS_FAIL)) {
Log4jUtil.error("调用目标接口返回状态为0====>处理失败");
}
//处理成功
else if (StringUtils.equals(status,MsgConstantType.STATUS_SUCCESS)) {
Log4jUtil.info("调用目标接口返回状态为1====>开始处理");
//获取合格证公告 json数组
JSONObject hgzgg = jsonObject.getJSONObject("hgzgg");
//json格式转换成 List集合
List<BizDtNotice> hgzggList = JSON.parseArray(hgzgg.toJSONString(), BizDtNotice.class);
//处理合格证信息 入库操作
this.handleList(hgzggList);
}
Log4jUtil.info("处理基础公告数据接口结束");
//返回接口响应结果
return jsonObject;
}
/**
* 处理基础公告结果集
*
* @param list 操作行
*/
private void handleList(List<BizDtNotice> list) {
Log4jUtil.info("开始处理合格证信息结果集");
//定义影响行数
int rows = 0;
//定义影响行数
for (BizDtNotice e : list) {
try {
//根据主键查找某一行
if (bizDtNoticeDao.selectOneByKey(e) == null) {
//不存在,则插入
rows += bizDtNoticeDao.insertByEntity(e);
//处理合格证信息,存入进合格证信息推送表
loadToQueue(e);
}
} catch (Exception err) {
Log4jUtil.error("插入合格证信息推送表异常---产品编号为:"+e.getCpno(),err);
}
}
Log4jUtil.info("影响的行数:" + rows);
}
/**
* 存入合格证信息推送队列
*/
private void loadToQueue(BizDtNotice bizDtNotice){
//封装公告下载实体
BizDtQueuePushCertificate e = new BizDtQueuePushCertificate();
e.setVin(bizDtNotice.getVnm());
e.setXrsj(new Date());
e.setScbj(MsgConstantType.UPLOAD_CERTIFICATE_WAIT);
//存表
try{
Integer rows = bizDtQueuePushCertificateDao.insertByEntity(e);
Log4jUtil.info("存入合格证信息推送队列 影响行"+rows);
}catch (Exception err) {
Log4jUtil.error("插入合格证信息推送表异常---产品公告号:"+bizDtNotice.getCpno(),err);
}
}
}
第四步:调用策略、选择策略
我这样在类初始化时,在对象里只调用这一种策略方式更方便
//API执行器 实例下载合格证信息策略 在这里运行时选择策略
TaskApiExecutor executor = new TaskApiExecutor(new DownBasicNoticeDataStrategy());
//TaskApiExecutor executor = new TaskApiExecutor(new DownCertificateChangeStrategy());
//TaskApiExecutor executor = new TaskApiExecutor(new DownCertificateInfoStrategy());
//调用执行器
JSONObject execute = executor.execute(jsonObject);
这里还有一种调用方法是在方法中选择策略
直接在调用方法里使用策略。不必刻意定义使用策略的类
void chooseStrategy(Strategy strategy);