项目重构:设计模式(策略模式)
策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。
本文参考文章 设计模式最佳套路—— 愉快地使用策略模式
由于项目中,有个需求,通过计费引擎对不同类型进行计费,但是每个类型的计费参数又不一样,一开始通过if-else解决
if (typeA) {
逻辑1
} else if (typeB) {
逻辑2
} else if (typeC) {
逻辑3
} else {
逻辑4
}
虽然问题解决了,但是每次我们添加一个类型就要修改这段代码,而且代码也不美观;所以,对代码进行了重构
在spring框架中,我们可以通过getBean获取到Spring容器中已初始化的bean,如果我们实现了一个计算参数的接口,然后获取到不同的类型其实现类,这样就可以避免我们增加一个类型就要修改if-else分支
流程图
获取计费接口
public interface FeeChargingParamsHandler {
/**
* FeeChargingParamsHandler :: getChargingType
* <p>TO:获得计费类型
* <p>DO:确定一个唯一性的计费参数方法类型,相当于beanName
* <p>HISTORY: 2020/11/28 liuha : Created.
*
* @return String 计费数据实现类型
*/
String getChargingType();
/**
* FeeChargingParamsHandler :: getChargingData
* <p>TO:获取记录计费数据
* <p>DO:返回ChargingDataVO格式数据
* <p>HISTORY: 2020/11/28 liuha : Created.
*
* @param originalRecordId 计费记录id
* @return ChargingDataVO 计费数据
*/
ChargingDataVO getChargingData(String originalRecordId);
}
返回参数的类
@Data
public class ChargingDataVO {
/**
* 参数的名称
*/
private String name;
/**
* 参数类型
*/
private String type;
/**
* 参数值
*/
private List<List<Map>> value;
/**
* ChargingDataVO:: addMApValue
* <p>TO:添加value值
* <p>DO:初始化value,添加值到value
* <p>HISTORY: 2020/11/28 liuha : Created.
*
* @param mapList 变量
*/
public void addMApValue(List<Map> mapList) {
//初始化数据
if (this.value == null) {
this.value = new ArrayList<>();
}
this.value.add(mapList);
}
}
这里是由于业务的要求,参数值一个二维码数组,这里避免掉业务会乱传入数据,所以才会使用了 List<List<Map>
的一个数据结构
建立简单工厂
@Component
public class FeeChargingHandlerFactory implements InitializingBean, ApplicationContextAware {
private static final
Map<String, FeeChargingParamsHandler> FEE_CHARGING_HANDLER_MAP = new HashMap(8);
private ApplicationContext appContext;
/**
* FeeChargingHandlerFactory ::
* <p>TO:传入类型获取对应的处理器
* <p>HISTORY: 2020/11/28 liuha : Created.
* @param chargingType 计费类型
* @return 返回类型对应的处理器
*/
public FeeChargingParamsHandler getHandler(String chargingType) {
return FEE_CHARGING_HANDLER_MAP.get(chargingType);
}
@Override
public void afterPropertiesSet() {
// 将 Spring 容器中所有的 FeeChargingParamsHandler 注册到 FEE_CHARGING_HANDLER_MAP
appContext.getBeansOfType(FeeChargingParamsHandler.class)
.values()
.forEach(handler -> FEE_CHARGING_HANDLER_MAP.put(handler.getChargingType(), handler));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
appContext = applicationContext;
}
}
实现计费参数接口
@Component
public class TestFeeChargingParamsHandler implements FeeChargingParamsHandler {
/**
* TestFeeChargingParamsHandler:: getChargingType
* <p>TO:获得实现方法的计费数据实现类型
* <p>DO:确定一个唯一性的计费参数方法类型,相当于beanName
* <p>HISTORY: 2020/11/28 liuha : Created.
*
* @return String 计费数据实现类型
*/
@Override
public String getChargingType() {
return "test";
}
/**
* TestFeeChargingParamsHandler:: getChargingData
* <p>TO:获取记录计费数据
* <p>DO:返回ChargingDataVO格式数据
* <p>HISTORY: 2020/11/28 liuha : Created.
*
* @param originalRecordId 计费记录id
* @return ChargingDataVO 计费数据
*/
@Override
public ChargingDataVO getChargingData(String originalRecordId) {
ChargingDataVO chargingDataVO = new ChargingDataVO();
chargingDataVO.setName("管道");
chargingDataVO.setType("String");
List<Map> mapList = new ArrayList<>();
Map<String, String> map = new HashMap<>();
map.put("name", "铺设方式");
map.put("value", "架空");
mapList.add(map);
Map<String, String> map1 = new HashMap<>();
map1.put("name", "压力管道级别");
map1.put("value", "GC3");
mapList.add(map1);
Map<String, String> map2 = new HashMap<>();
map2.put("name", "管道外径");
map2.put("value", "60");
mapList.add(map2);
Map<String, String> map3 = new HashMap<>();
map3.put("name", "管道长度]);
map3.put("value", "1");
mapList.add(map3);
chargingDataVO.addMApValue(mapList);
return chargingDataVO;
}
}
代码调用
在业务中中,我们通过 FeeChargingHandlerFactory 来获取类型实现的计费接口实现类,从而获取到计费的参数:
@Autowired
private FeeChargingHandlerFactory handlerFactory;
......... 业务代码
/**
* ITjFeeChargingServiceImpl:: getChargingData
* <p>TO:获取业务实现计费参数数据
* <p>DO:从handlerFactory获取到FeeChargingParamsHandler;调用实现类;返回结果
* <p>HISTORY: 2020/11/29 liuha : Created.
*
* @param recordId 计费的业务数据id
* @param type 实现类的type类型
* @return ChargingParamData 类型的计费参数
*/
private ChargingParamData getChargingData(String recordId, String type) {
//查找策略实现
FeeChargingParamsHandler handler = handlerFactory.getHandler(type);
if (handler == null) {
throw new JeecgBootException("未找到实现二维表取的实现类");
}
return handler.getChargingData(recordId);
}
这样便可以解决了多层if-else的代码了,如果添加了一个类型,只需要添加一个新的策略实现即可,其实还有一个问题,由于工厂模式中,我们使用了map存入实现类,
@Override
public void afterPropertiesSet() {
// 将 Spring 容器中所有的 FeeChargingTableParamsHandler 注册到 FEE_CHARGING_HANDLER_MAP
appContext.getBeansOfType(FeeChargingTableParamsHandler.class)
.values()
.forEach(handler -> FEE_CHARGING_HANDLER_MAP.put(handler.getChargingType(), handler));
}
如果存在一样的类型参数,就会把之前存入的策略实现,替换掉,所以可以在遍历过程中判断是否存在重复的type,由于我是,内部方法调用,可以不做校验。另外,如果类型可以确定,使用switch-case的方法也实现分支,策略模式在项目还可以和模板方法结合使用,这样就可以解决主体方法一样,但是部分代码存在分支的情况,比如业务实现工作