实际业务中使用策略模式对代码进行重构
一.业务描述最近在负责公司一个语音的微服务模块优化,这个模块主要的业务是:1.天猫精灵、小度、若琪、小京鱼、小爱同学、思必驰这些第三方音响对我们的用户进行oauth2/JWT授权; 2.这些第三方音响服务调用我们的设备发现接口对公司的设备信息在第三方平台进行一个存储;3.第三方平台对用户发出的语音进行解析,然后识别出需要控制的设备再调用我们的设备控制接口对公司的设备进行控制;
二.需要优化的点
上述发现、控制接口分别写了五个API,并且在Controller层有着大量的业务校验,然后再在Controller层调用Service层的设备发现、控制方法;这些业务校验的逻辑一模一样;
字有点不好看,兄dei们献丑了,嘿嘿
三.优化(为方便演示这个举三个语音平台的例子)
1.对不同平台的业务实现代码进行重构(图中的②)
① 将之前的三个语音接口提取为同一个策略接口命名为:(VoiceStrategyService)
public interface VoiceStrategyService {
/**
* @description:语音控制API
* @param:
* @return: com.alibaba.fastjson.JSONObject
* @author: zhouhong
* @date: 2023/5/18 9:34
*/
JSONObject operateApi(@RequestBody JSONObject jsonObject);
}② 其他几个实现类实现 (VoiceStrategyService) 这一个接口
其他几个语音实现类实现上面的那个策略接口,每个策略实现类对应一个业务场景,实现具体的方法逻辑。
@Service
@Log4j2
public class AliGenieServiceImpl implements VoiceStrategyService {
@Override
public JSONObject operateApi(JSONObject jsonObject) {
log.info("天猫精灵-设备发现/控制成功!");
return null;
}
}@Service
@Log4j2
public class DuerOSServiceImpl implements VoiceStrategyService {
@Override
public JSONObject operateApi(JSONObject jsonObject) {
log.info("小度-设备发现/控制成功!");
return null;
}
}@Service
@Log4j2
public class RokidServiceImpl implements VoiceStrategyService {
@Override
public JSONObject operateApi(JSONObject jsonObject) {
log.info("若琪-设备发现/控制成功!");
return null;
}
}@Service
@Log4j2
public class RokidServiceImpl implements VoiceStrategyService {
@Override
public JSONObject operateApi(JSONObject jsonObject) {
log.info("若琪-设备发现/控制成功!");
return null;
}
}③ 接下来,定义一个上下文类(VoiceStrategyContext),该类持有一个策略对象,并提供一个方法用于设置策略对象
/**
* @description: 语音策略上下文
* @author: zhouhong
* @date: 2023/5/20 14:27
* @version: 1.0
*/
public class VoiceStrategyContext {
@Resource
private VoiceStrategyService voiceStrategyService;
private void setVoiceStrategy(VoiceStrategyService voiceStrategyService) {
this.voiceStrategyService = voiceStrategyService;
}
private JSONObject executeStrategy(JSONObject jsonObject) {
if (voiceStrategyService != null) {
return voiceStrategyService.operateApi(jsonObject);
}
return null;
}
/**
* @description: 根据传过来的KEY值选择具体的策略
* @return: com.alibaba.fastjson.JSONObject
* @author: zhouhong
* @date: 2023/5/20 15:03
*/
public JSONObject executeStrategyByKey(String key, JSONObject jsonObject) {
switch (key) {
case "aliGenie" : {
this.setVoiceStrategy(new AliGenieServiceImpl());
return this.executeStrategy(jsonObject);
}
case "duerOS" : {
this.setVoiceStrategy(new DuerOSServiceImpl());
return this.executeStrategy(jsonObject);
}
case "rokid" : {
this.setVoiceStrategy(new RokidServiceImpl());
return this.executeStrategy(jsonObject);
}
default: {
return null;
}
}
}
}这个如果在下一层调用时知道自己需要调用哪个策略,那么 executeStrategyByKey() 方法可以直接忽略,具体调用如下所示:
/**
* @description: 测试类
* @author: zhouhong
* @date: 2023/5/20 15:06
* @version: 1.0
*/
public class TextMain {
public static void main(String[] args) {
VoiceStrategyContext voiceStrategyContext = new VoiceStrategyContext();
JSONObject jsonObject = new JSONObject();
// 天猫精灵
voiceStrategyContext.setVoiceStrategy(new AliGenieServiceImpl());
voiceStrategyContext.executeStrategy(jsonObject);
// 小度
voiceStrategyContext.setVoiceStrategy(new DuerOSServiceImpl());
voiceStrategyContext.executeStrategy(jsonObject);
// 若琪
voiceStrategyContext.setVoiceStrategy(new RokidServiceImpl());
voiceStrategyContext.executeStrategy(jsonObject);
}
}结果:
15:12:38.474 INFO com.zhouhong.designpattern.strategy.service.impl.AliGenieServiceImpl - 天猫精灵-设备发现/控制成功!
15:12:38.477 INFO com.zhouhong.designpattern.strategy.service.impl.DuerOSServiceImpl - 小度-设备发现/控制成功!
15:12:38.477 INFO com.zhouhong.designpattern.strategy.service.impl.RokidServiceImpl - 若琪-设备发现/控制成功!
2.对开始那张图的 ① 进行代码提取
这个比较简单,直接选中需要提取的代码块 Windows系统中 按住Ctrl 和 Alt 再加 M 键,就可以快速的将需要提取的代码从方法中抽离出来,然后新建一个Service层接口对其进行实现即可,主要示例代码如下:
/**
* @description: 语音控制公共方法抽取
* @author: zhouhong
* @date: 2023/5/17 10:58
* @version: 1.0
*/
public interface VoiceCommonApiService {
/**
* @description:语音控制公共方法抽取 -- RequestBody格式
* @param:
* @return: com.alibaba.fastjson.JSONObject
* @author: zhouhong
* @date: 2023/5/17 11:00
*/
JSONObject voiceRequestBodyCommonApi(JSONObject jsonObject, String platform);
}/**
* @description: 语音公共方法抽离
* @author: zhouhong
* @date: 2023/5/17 11:00
* @version: 1.0
*/
@Service
@Log4j2
public class VoiceCommonApiServiceImpl implements VoiceCommonApiService {
@Resource
private VoiceStrategyContext voiceStrategyContext;
@Override
public JSONObject voiceRequestBodyCommonApi(JSONObject jsonObject, String platform) {
log.info("大量逻辑校验代码......");
voiceStrategyContext.executeStrategyByKey(platform, jsonObject);
log.info("其他业务代码......");
return null;
}
}优化完工
四.设计模式总结
1.简介
策略模式是一种行为型设计模式,它允许在运行时选择算法的行为。该模式将不同的算法封装在各自独立的策略类中,使得它们可以互相替换,而不会影响到客户端代码。
2.主要参与角色
[*]环境类(Context):环境类持有一个策略对象,并在需要执行算法时调用策略对象的方法。它提供了一个接口供客户端代码设置策略对象。
[*]抽象策略类(Strategy):定义了策略对象的接口或抽象类。它描述了算法的通用行为,可以包含算法的输入参数。
[*]具体策略类(Concrete Strategy):实现了策略接口或继承了抽象策略类,并提供了具体的算法实现
3.工作流程
[*]客户端代码创建一个环境对象(Context)。
[*]客户端根据需求选择一个具体策略类,并将其设置到环境对象中。
[*]环境对象在需要执行算法的时候,调用所持有的策略对象的方法。
[*]策略对象根据自身的算法实现进行处理,并返回结果给环境对象。
[*]客户端通过环境对象获取算法的结果。
4.使用场景
[*]多种算法选择:当有多个算法可供选择,且需要在运行时动态选择其中一种算法时,可以使用策略模式。例如,在图像处理中,可以根据不同的要求选择不同的压缩算法。
[*]避免条件语句:当代码中存在大量的条件语句用于根据不同条件执行不同的行为时,可以考虑使用策略模式来替代这些条件语句。策略模式将每种行为封装在单独的策略类中,使代码更加清晰、可维护。
[*]动态配置行为:当需要动态地配置对象的行为时,可以使用策略模式。例如,在电商系统中,可以根据用户的会员级别,动态选择不同的折扣策略。
[*]可扩展性:策略模式提供了一种可扩展的方式,允许添加新的策略类来满足新的需求,而无需修改现有代码。这种灵活性使得策略模式在需要频繁添加新的算法或行为的情况下非常有用。
[*]单一职责原则:策略模式可以将不同的算法或行为分离到各自的策略类中,遵循了单一职责原则,使得每个类只关注自己的策略实现。
5.优缺点
优点:
[*]提供了一种清晰的方式来管理不同算法的实现,并将其与客户端代码解耦。
[*]策略类可以灵活地替换和扩展,不会对客户端代码造成影响。
[*]提高了代码的可维护性和可读性,减少了大量的条件语句。
缺点:
[*]增加了类的数量,每个具体策略类都需要单独实现一个类。
五.示例代码地址
https://github.com/Tom-shushu/work-study.git 代码中的 design-pattern 项目
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]