前言
一般来说,项目上常常会碰到,某一个功能,根据传过来的业务类型,返回不同的结果,基本上是业务类型的逻辑或计算方法不一致,而这个业务类型不是通常固定的字典数据,时不时会因业务的需要,进行增加,当碰到这种情况,基本上就可以使用策模式来解决扩展问题,而不是在一个类里,定义不同的方法来实现。
举个例子
在个人开发过的项目中,因项目需要,与终端通信时,有部分使用HTTP协议不能满足需要,重新自定义了一个协议ufo(具体协议就不在此说明),约定了统一的参数,根据传输入数据中,指令部分的Code,返回不同数据。
从实现的角度来说,首先想的就是if-else或switch-case来判断Code,调用不同的方法实现。
@Service
public class UfoService {
public PacketData processData(PacketData input) {
switch (input.getCode()) {
// A指令
case "A":
return A(input);
// B指令
case "B":
return B(input);
// C指令
case "C":
return C(input);
}
}
private PacketData A(PacketData input){
...
}
private PacketData B(PacketData input){
...
}
private PacketData C(PacketData input){
...
}
}
因指令一开始在设计时,是没办法定义全的,根据产品经理对产品功能的迭代,时不时需要增加指令来满足功能的需要,如果把这些方法写在一个类里,那么每次增加指令都需要变动这个类,这个对程序的扩展性来说,不太友好。
改造
那么使用策略模式的话,可以解决扩展性问题,同时结合Spring的注入,对进行switch-case进行优化
1. 定义接口
接口里有一个方法(一个还是多个根据实际需要),此方法的定义就是处理数据,并返回结果:
public interface BaseProcessStrategy {
/**
* 数据处理
* PacketData 为自定义数据转的对象
*/
public PacketData process(PacketData input);
}
2. 实现接口
各指令实现BaseProcessStrategy接口就可以(注意:因下一步环境类的构造函数中,注入所有BaseProcessStrategy接口bean,所以在使用注解@Component时需要显示指定名称),如:
/**
* A指令
*/
@Component("StrategyA")
public class StrategyA implements BaseProcessStrategy {
/**
* 数据处理
* PacketData 为自定义数据转的对象
*/
@Override
public PacketData process(PacketData input) {
PacketData output = new PacketData();
... // 数据处理省略
output.setData(data);
return output;
}
}
3. 定义环境
定义Context,用于连接上下文,也就是使用方用的类
/**
* 上下文
*/
@Component
public class UfoContext {
private final static String PREFIX = "strategy";
private final Map<String, BaseProcessStrategy> strategyMap = new ConcurrentHashMap<>();
/**
* Spring自动注入所以实现了BaseProcessStrategy接口的Bean
*/
@Autowired
public UfoContext(Map<String, BaseProcessStrategy> strategyMap) {
this.strategyMap.clear();
strategyMap.forEach((k, v)-> this.strategyMap.put(k, v));
}
/**
* 执行指令
* @param packet 入包
* @return 出包
*/
public PacketData execute(PacketData input) {
return strategyMap.get(PREFIX + packet.getCode()).process(input);
}
}
4. 调用方代码改造
@Service
public class UfoService {
@Autowired
private UfoContext context;
public PacketData processData(PacketData input) {
return context.execute(input);
}
}
之后,这部分代码就可以不用动,只要增加实现BaseProcessStrategy接口的指令类就可以。