策略模式工作中的运用

一:前景提要

阅读本文章第一段-第四段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,还不赶紧学习!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值