文章目录
一:前景提要
阅读本文章第一段-第四段10分钟,第五段10分钟
前景:
工作快一年了,近期的需求需要改动之前写的代码,新增加功能,看到一处代码,非常冗余,好多if else 判断,并且里面的逻辑是可重用的、可统一管理的。这是谁写的垃圾代码,于是我打开annotate with git blame
发现是我去年写的代码。咳咳,情有可原,当时业务没那么复杂(其实当时不会策略模式)
代码业务场景:
判断多种控件上下游关系,校验控件参数。
业务代码:
/**
* 遍历 nodeDataArray 初始化数据
*
* @param nodes 初始数据
* @param rootCustomerMap 上游节点系统变量
* @param checkCustomerMap 下游节点系统变量
* @param errorExampleMap 下游节点信息
*/
private void initNodeDataArray(List<Map<String,Object>> nodes,HashMap<Integer, Set<Integer>> rootCustomerMap,HashMap<Integer, Set<Integer>> checkCustomerMap,HashMap<Integer, String> errorExampleMap){
nodes.forEach(node -> {
String category = MapUtils.getString(node, "category");
Integer nodeId = MapUtils.getInteger(node, "key");
Map<String, Object> mData = (Map<String, Object>) MapUtils.getObject(node, "mdata");
if (NodeTypeEnum.START.getName().equals(category) || NodeTypeEnum.WAIT.getName().equals(category)) {
// start : mData-execute-customerAction(IN_PAY,OUT_PAY),如果不为空保存
// wait : mData-customerAction(OUT_PAY),如果不为空保存
Map<String, Object> execute = (Map<String, Object>) MapUtils.getObject(mData, "execute");
List<String> customerAction = (List<String>) MapUtils.getObject(execute == null ? mData : execute, "customerAction");
if (CollectionUtils.isNotEmpty(customerAction)) {
Set<Integer> varDefIds = new HashSet<>();
for (String customer : customerAction) {
VarDef varDef = new VarDef();
varDef.setName(SYSTEM_GLOBAL_PIN+customer);
List<VarDef> listByTagValueAndNameAndParentId = varDefService.findListByTagValueAndNameAndParentId(varDef);
if (CollectionUtils.isNotEmpty(listByTagValueAndNameAndParentId)){
varDefIds.add(listByTagValueAndNameAndParentId.get(0).getId().intValue());
}
}
if (!varDefIds.isEmpty()) {
rootCustomerMap.put(nodeId,varDefIds);
}
}
} else if (NodeTypeEnum.SD_SMS.getName().equals(category) || NodeTypeEnum.SD_JPUSH.getName().equals(category) ) {
// sms :mData-params(数组)-smsOptionValue-code(type==VARIABLE)
// push : mData-params(数组)-pushOptionValue-code(type==VARIABLE)
List<Map<String, Object>> params = (List<Map<String, Object>>) MapUtils.getObject(mData, "params");
if (CollectionUtils.isNotEmpty(params)) {
Set<Integer> codes = new HashSet<>();
for (Map<String, Object> param : params) {
Map<String, Object> smsOptionValue = (Map<String, Object>) MapUtils.getObject(param, NodeTypeEnum.SD_JPUSH.getName().equals(category) ? "pushOptionValue" : "smsOptionValue");
String type = MapUtils.getString(smsOptionValue, "type");
if ("VARIABLE".equals(type)) {
Integer code = MapUtils.getInteger(smsOptionValue, "code");
if (code != null) {
VarDef varDef = new VarDef();
varDef.setId(Long.valueOf(code));
List<VarDef> listByTagValueAndNameAndParentId = varDefService.findListByTagValueAndNameAndParentId(varDef);
if (CollectionUtils.isNotEmpty(listByTagValueAndNameAndParentId)){
codes.add(listByTagValueAndNameAndParentId.get(0).getParentId().intValue());
}
}
}
}
if (!codes.isEmpty()) {
checkCustomerMap.put(nodeId,codes);
errorExampleMap.put(nodeId, category);
}
}
} else if (NodeTypeEnum.IF_ELSE.getName().equals(category)) {
// elseIf : mData-list(数组)-list(数组)- variableId(bizType==tag_var)
List<Map<String, Object>> lists = (List<Map<String, Object>>) MapUtils.getObject(mData, "list");
if (CollectionUtils.isNotEmpty(lists)) {
Set<Integer> codes = new HashSet<>();
for (Map<String, Object> list : lists) {
List<Map<String, Object>> tagValMapList = (List<Map<String, Object>>) MapUtils.getObject(list, "list");
if (CollectionUtils.isNotEmpty(tagValMapList)) {
for (Map<String, Object> tagValMap : tagValMapList) {
if ("tag_var".equals(MapUtils.getString(tagValMap,"bizType")) && MapUtils.getInteger(tagValMap,"isSystem").equals(1)) {
Integer code = MapUtils.getInteger(tagValMap, "variableId");
if (code != null) {
VarDef varDef = new VarDef();
varDef.setId(Long.valueOf(code));
List<VarDef> listByTagValueAndNameAndParentId = varDefService.findListByTagValueAndNameAndParentId(varDef);
if (CollectionUtils.isNotEmpty(listByTagValueAndNameAndParentId)){
codes.add(listByTagValueAndNameAndParentId.get(0).getParentId().intValue());
}
}
}
}
}
}
if (!codes.isEmpty()) {
checkCustomerMap.put(nodeId,codes);
errorExampleMap.put(nodeId, category);
}
}
}else if (NodeTypeEnum.WECHAT.getName().equals(category)){
// mdata-params(json list)-weChatOptionValue-(type && code)
String paramsJson = MapUtils.getString(mData, "params");
List<Map> params = JSON.parseArray(paramsJson, Map.class);
if (CollectionUtils.isNotEmpty(params)){
Set<Integer> codes = new HashSet<>();
for (Map param : params) {
Map<String, Object> weChatOptionValue = (Map<String, Object>)MapUtils.getObject(param,"weChatOptionValue");
String type = MapUtils.getString(weChatOptionValue,"type");
if ("VARIABLE".equals(type)){
Integer code = MapUtils.getInteger(weChatOptionValue,"code");
if (code != null) {
VarDef varDef = new VarDef();
varDef.setId(Long.valueOf(code));
List<VarDef> listByTagValueAndNameAndParentId = varDefService.findListByTagValueAndNameAndParentId(varDef);
if (CollectionUtils.isNotEmpty(listByTagValueAndNameAndParentId)){
codes.add(listByTagValueAndNameAndParentId.get(0).getParentId().intValue());
}
}
}
}
if (!codes.isEmpty()) {
checkCustomerMap.put(nodeId,codes);
errorExampleMap.put(nodeId, category);
}
}
}else if (NodeTypeEnum.DATA_INTERACTION.getName().equals(category) ) {
// sms :mData-params(数组)-smsOptionValue-code(type==VARIABLE)
// push : mData-params(数组)-pushOptionValue-code(type==VARIABLE)
String mq = MapUtils.getString(mData,Constant.MQ_TEMPLATE);
Map<String,Object> mqTemplateMap = JSON.parseObject(mq,Map.class);
String paramString = MapUtils.getString(mqTemplateMap, Constant.PARAMS);
JSONArray params = JSON.parseArray(paramString);
if (CollectionUtils.isNotEmpty(params)) {
Set<Integer> codes = new HashSet<>();
if (params != null && params.size() > 0) {
for (int i = 0; i < params.size(); i++) {
JSONObject dataJson = params.getJSONObject(i);
String smsOptionValue = dataJson.getString(Constant.SMS_OPTION_VALUE);
if (StringUtils.isNotBlank(smsOptionValue)) {
JSONObject jsonObject = JSON.parseObject(smsOptionValue);
String type = MapUtils.getString(jsonObject, "type");
if ("VARIABLE".equals(type)) {
Integer code = MapUtils.getInteger(jsonObject, "code");
if (code != null) {
VarDef varDef = new VarDef();
varDef.setId(Long.valueOf(code));
List<VarDef> listByTagValueAndNameAndParentId = varDefService.findListByTagValueAndNameAndParentId(varDef);
if (CollectionUtils.isNotEmpty(listByTagValueAndNameAndParentId)) {
codes.add(listByTagValueAndNameAndParentId.get(0).getParentId().intValue());
}
}
}
}
}
}
if (!codes.isEmpty()) {
checkCustomerMap.put(nodeId,codes);
errorExampleMap.put(nodeId, category);
}
}
}
});
}
你看看吧,多恶心,而且扩展性差,违反了开闭原则。我现在后悔莫及啊,咋弄,改造吧。整天划水也玩烦了,那行,我们分析一下:
你看啊
这么多else if 判断 可以利用面向对象方法
ok 策略模式设计完了,简单吧
我们用面向对象的思想封装这些代码块,便于维护
至于我们怎么封装为对象?
我们观察其特点,他们会有共同的属性来区分,也就是if的判断条件
我们讲if的判断条件维护在枚举类中
那我们只需要一个接口
让这么多 if 判断实现接口
在for循环的过程中,具体使用接口的哪个实现类,根据他的枚举值判断
这样我们在for循环就只写两行代码就搞定了
第一行代码是:获取接口的实现类
第二行代码是:接口中的方法调用
我们分析完了,我们缕缕上面啰嗦那么多,总结一下步骤
- 声明一个接口,接口中定义方法
- 写几个接口的实现类,也就是我们if else if 判断的条件
- 写一个枚举类,可以根据枚举码获取对应的实现类(工厂模式实现)
- 写一个工厂方法,根据枚举类返回接口实现类
ok,步骤就是这么多,我们先把代码精简一下,体会策略模式精妙所在
二:未使用策略模式前代码
我们讲前情提要中的冗杂的代码简化一下,方便观察
2.1 实体类
package com.runoob.factory.beforefactory;
/**
* @author: vfudongyang
* @createTime: 2021年06月30日 14:32:00
* @Description:
*/
public class EntityDemo {
/**
* 枚举码
*/
private String code;
/**
* 姓名
*/
private String name;
public EntityDemo(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.2 枚举类
package com.runoob.factory.beforefactory;
/**
* @author: vfudongyang
* @createTime: 2021年06月30日 11:24:00
* @Description: 业务枚举类
*/
public enum EnumDemo {
FIRST("001","第一个"),
SECOND("002","第二个");
private final String code;
private final String desc;
EnumDemo(String code, String desc){
this.code = code;
this.desc = desc;
}
public static EnumDemo getByCode(String code){
if (code == null){
return null;
}
for (EnumDemo value : EnumDemo.values()) {
if (value.getCode().equals(code)){
return value;
}
}
return null;
}
public String getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
2.3 使用类
package com.runoob.factory.beforefactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @author: vfudongyang
* @createTime: 2021年06月30日 14:26:00
* @Description:
*/
@Component("BeforeMainDemo")
public class MainDemo implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
List<EntityDemo> paramList = new ArrayList<>();
EntityDemo firstDemo = new EntityDemo("001", "firstType");
paramList.add(firstDemo);
EntityDemo secondDemo = new EntityDemo("002", "secondType");
paramList.add(secondDemo);
for (EntityDemo entityDemo : paramList) {
if ("001".equals(entityDemo.getCode())) {
// 假如这个业务逻辑很复杂 有100行 而且可能被复用这个逻辑
System.out.println("before:按照规则一打印:" + entityDemo.getName());
} else if ("002".equals(entityDemo.getCode())) {
// 假如这个业务逻辑很复杂 有100行 而且可能被复用这个逻辑
System.out.println("before:按照规则二打印:" + entityDemo.getName());
}
}
}
}
真简化,为了体现策略模式主干,我们忽略业务逻辑
三:使用策略模式后的代码
实体类、枚举类我们还用之前的(2.1、2.2的)
3.1 写一个接口
package com.runoob.factory.afterfactory;
/**
* @author: vfudongyang
* @createTime: 2021年06月30日 11:34:00
* @Description:
*/
public interface InterfaceDemo {
void print(String name);
EnumDemo getEnum();
}
3.2 写两个实现类
3.2.1 实现类1
package com.runoob.factory.afterfactory;
import org.springframework.stereotype.Component;
/**
* @author: vfudongyang
* @createTime: 2021年06月30日 11:35:00
* @Description:
*/
@Component
public class FirstImpl implements InterfaceDemo {
@Override
public void print(String name) {
System.out.println("按照规则一打印:"+name);
}
@Override
public EnumDemo getEnum() {
return EnumDemo.FIRST;
}
}
3.2.2 实现类2
package com.runoob.factory.afterfactory;
import org.springframework.stereotype.Component;
/**
* @author: vfudongyang
* @createTime: 2021年06月30日 11:36:00
* @Description:
*/
@Component
public class SecondImpl implements InterfaceDemo {
@Override
public void print(String name) {
System.out.println("按照规则二打印:"+name);
}
@Override
public EnumDemo getEnum() {
return EnumDemo.SECOND;
}
}
3.3 写一个工厂方法
这是spring的项目,可以利用spring的特性,请看
package com.runoob.factory.afterfactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author: vfudongyang
* @createTime: 2021年06月30日 11:30:00
* @Description: 利用spring反射创建工厂实例
*/
@Component
public class FactoryDemo implements ApplicationContextAware {
private static Map<EnumDemo,InterfaceDemo> demoMap;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
demoMap = new HashMap<>();
Map<String, InterfaceDemo> beansOfType = applicationContext.getBeansOfType(InterfaceDemo.class);
for (Map.Entry<String, InterfaceDemo> entry : beansOfType.entrySet()) {
EnumDemo curEnum = entry.getValue().getEnum();
if (curEnum == null){
continue;
}
demoMap.put(curEnum,entry.getValue());
}
}
public static < T extends InterfaceDemo> T getDemoImpl(EnumDemo curEnum){
return (T)demoMap.get(curEnum);
}
}
3.4 使用类-看下效果
package com.runoob.factory.afterfactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @author: vfudongyang
* @createTime: 2021年06月30日 14:26:00
* @Description:
*/
@Component
public class MainDemo implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
List<EntityDemo> paramList=new ArrayList<>();
EntityDemo firstDemo = new EntityDemo("001", "firstType");
paramList.add(firstDemo);
EntityDemo secondDemo = new EntityDemo("002", "secondType");
paramList.add(secondDemo);
for (EntityDemo entityDemo : paramList) {
EnumDemo curEnum = EnumDemo.getByCode(entityDemo.getCode());
InterfaceDemo demoImpl = FactoryDemo.getDemoImpl(curEnum);
demoImpl.print(entityDemo.getName());
}
}
}
四:总结
如果再新增,我们只需新增一个类,实现接口,实现接口对应的方法即可,开闭原则完美保持,面向对象设计真能!
如果看不懂,可以留言加私信,完整代码放到码云了
源码地址请点击-跳转到码云
五:请欣赏
拜拜: 一群lsp,还不赶紧学习!!!