需求分析
公司做物联网系统的,使用nettry进行设备连接,对设备进行数据采集,根据设备的协议对数据进行解析,解析完成之后存放数据库,但是不同厂家的设备协议不同。公司系统使用了使用了函数式编程的去写了一个解析类,所有的协议解析都在同一个方法类里面了,耦合度超高,不够优雅,我决定使用策略模式对它进行优化!
策略模式
下面是原理图,跟其他大佬的差不多,主要就是抽象策略接口、具体策略、环境三部分,这里不介绍策略模式了,建议去看看别的大佬写的好博客吧。
下面直接上代码!
代码实现
枚举类
现有的系统是根据协议长度来判断不同厂家的设备的,所以定义一个枚举类,len是协议的长度,beanName是具体策略的bean名称,通过传入的协议长度获取beanName,再获取具体策略的bean,去执行具体的解析方法。
public enum AnalysisEnum {
SONGXIA(101, "songXiaAnalysisStrategy", "松下"),
OTC(102, "OTCAnalysisStrategy", "OTC"),
JIANGNAN(103, "jiangNanAnalysisStrategy", "江南"),
;
private int len;
private String beanName;
private String desc;
AnalysisEnum(int len, String beanName, String desc) {
this.len = len;
this.beanName = beanName;
this.desc = desc;
}
public int getLen() {
return len;
}
public String getBeanName() {
return beanName;
}
public String getDesc() {
return desc;
}
public static AnalysisEnum getAnalysisEnum(Integer len) {
for (AnalysisEnum analysisEnum : AnalysisEnum.values()) {
if (analysisEnum.getLen() == len) {
return analysisEnum;
}
}
throw new RuntimeException("异常");
}
}
抽象策略接口和具体策略
public interface AnalysisStrategy {
void analysis();
}
@Component("jiangNanAnalysisStrategy")
public class JiangNanAnalysisStrategy implements AnalysisStrategy {
@Override
public void analysis() {
System.out.println("解析江南协议...");
}
}
@Component("OTCAnalysisStrategy")
public class OTCAnalysisStrategy implements AnalysisStrategy {
@Override
public void analysis() {
System.out.println("解析OTC协议...");
}
}
@Component("songXiaAnalysisStrategy")
public class SongXiaAnalysisStrategy implements AnalysisStrategy {
@Override
public void analysis() {
System.out.println("解析松下协议...");
}
}
环境
将抽象策略接口定义为Map<String, AnalysisStrategy>的value,通过spring的自动注入,所有的具体策略实现类都会被注入到map当中,key为beanId,即@Component指定的bean名称,配合AnalysisEnum,就可以通过协议长度获取具体策略执行具体方法,从而优雅地消除if-else了
@Component
public class AnalysisContext {
@Resource
private Map<String, AnalysisStrategy> selectorMap;
public void analysis(Integer len) {
AnalysisEnum analysisEnum = AnalysisEnum.getAnalysisEnum(len);
System.out.println("协议长度:" + len + " " + "设备:" + analysisEnum.getDesc());
selectorMap.get(analysisEnum.getBeanName()).analysis();
}
}
controller
@RestController
@Api(tags = "策略模式")
public class StrategyController {
@Autowired
private AnalysisContext analysisContext;
@GetMapping("/strategy")
@ApiOperation("策略模式测试接口")
public void test1(@RequestParam Integer len) {
analysisContext.analysis(len);
}
}
测试结果
总结
最优雅地策略模式,当然是实现了抽象策略接口之后就可以使用,但是目前本帅写的代码当中尽管优雅地消除了if-else,但不符合OOP原则,不过没办法,系统中就必须得通过协议长度去判断不同的协议,而spring中注入具体策略类是通过beanId去实现的,无法通过协议长度直接拿到具体策略类。有一个好的方法就是通过配置文件或者数据库去配置协议长度和beanId的关系,这样子以后再有扩展不同厂家的协议之后就可以不需求改动源代码,而只需要在配置文件或数据库中添加协议长度和beanId的映射关系即可