java设计模式之策略模式实操

一、背景

临床服务项目流向规则匹配,比如说医生开一个"CT"检查,该检查应该由哪个科室来执行,是通过流向规则配置来决定的,具体配置如下图:

通过相关的条件匹配,最终找到流向科室。

二、设计思路

有几个注意的点:

(1)条件可能会变化,比如增加条件,减少条件;

(2)条件可以单选、多选、输入字符串,也就是说条件值可能是等于、包含、不等于、不包含、大于、小于、自定义输入值;

如何来设计更好的满足以上需求,从以下几个方面:

(1)表结构层面:既然条件有可能变化,那么条件名称和条件值不能设计成一个个固定的字段,这样如果如果增减或者减少条件就不必要去修改表结构;那么具体怎么设计呢?表结构设计成通用结构,包含三个主要字段:条件类型编码、条件名称、条件值;如果一个条件配置来两个或以上值,那么在表里面就对应了多条记录;

(2)代码设计模式:工厂模式+策略模式,每个配置的条件都需要去匹配,而每个条件的匹配可能相同或者不同,所以可以定义一个抽象类作为父类,每一个条件创建一个类,继承这个抽象类,抽象类主要有三个抽象方法:获取匹配器的类型代码、获取传入对象参数中该匹配器对应的入参值、条件匹配;通过工厂模式将所有匹配器进行初始化;

三、相关类定义

1、匹配器抽象类:获取匹配器的类型代码、是否自定义匹配算法、获取传入对象参数中该匹配器对应的入参值、条件匹配;

2、匹配器实现类一,配置了单个条件值;

3、匹配器实现类二,配置了多个条件值;

4、匹配器实现类三,自定义解析条件值并返回;

5、工厂类:批量创建匹配器实例;

6、匹配器持有类:对匹配器进行相关调度操作;

7、工具类:条件匹配;

四、类的详解

1、匹配器抽象类:CliFlowRuleMatcher

java 复制代码
    /**
     * 获取条件类型代码,表示我是哪个条件,由子类去实现
     * @return
     */
    protected abstract Long getConditionTypeCode();
    /**
     * 是否自定义匹配器
     * @return bool值
     */
    public boolean isCustomMatcher();
/**
     * 规则匹配
     * @param valueSet 配置值
     * @param regularOperationCode 运算符
     * @param condition 入参对象
     * @return 匹配结果
     */
    public boolean matchDefaultValue(Collection<Long> valueSet, FlowRuleCoreExtInputDTO condition, Long regularOperationCode) {
        // 先匹配单值
        Long value = singleValueGetter().apply(condition);
        if (Objects.nonNull(value)) {
            return MatchUtil.match(valueSet, value, regularOperationCode);
        }

        // 单值没实现再匹配多值
        Collection<Long> values = multiValueGetter().apply(condition);
        if (CollectionUtil.isNotEmpty(values)) {
            return MatchUtil.match(valueSet, values, regularOperationCode);
        }
        return true;
    }

/**
     * 自定义结果 规则匹配
     * @param customizeValSet
     * @param condition
     * @param regularOperationCode
     * @return
     */
    public boolean matchCustomizeValue(Collection<String> customizeValSet, FlowRuleCoreExtInputDTO condition, Long regularOperationCode) {

        // 先匹配单值
        Collection<String> value = singleCustomizeValueGetter().apply(condition);
        if (CollectionUtil.isNotEmpty(value)) {
            return MatchUtil.matchCustomizeValue(customizeValSet, value, regularOperationCode);
        }

        // 单值没实现再匹配多值
        Collection<String> values = multiCustomizeValueGetter().apply(condition);
        if (CollectionUtil.isNotEmpty(values)) {
            return MatchUtil.matchCustomizeValue(customizeValSet, values, regularOperationCode);
        }
        return true;
    }

2、条件匹配实现类一(单个条件值):BuIdMatcher

java 复制代码
@Component
public class BuIdMatcher extends CliFlowRuleMatcher {

    @Override
    public Long getConditionTypeCode() {
        return ConditionDeShortNameEnum.PRESCRIBED_BU_ID.getValue();
    }

    @Override
    public Function<FlowRuleCoreExtInputDTO, Long> singleValueGetter() {
        return FlowRuleCoreExtInputDTO::getBuId;
    }
}

3、条件匹配实现类二(多个条件值):CsTypeCodeMatcher

java 复制代码
@Component
public class CsTypeCodeMatcher extends CliFlowRuleMatcher {

    @Override
    public Long getConditionTypeCode() {
        return ConditionDeShortNameEnum.CS_TYPE_CODE.getValue();
    }

    @Override
    protected Function<FlowRuleCoreExtInputDTO, Collection<Long>> multiValueGetter() {
        return FlowRuleCoreExtInputDTO::getCsTypeCodeList;
    }
}

4、条件匹配实现类三(自定义获取配置的条件):RealIPMatcher

java 复制代码
@Component
public class RealIPMatcher extends CliFlowRuleMatcher {

    @Override
    public boolean isCustomMatcher() {
        return true;
    }

    @Override
    public Long getConditionTypeCode() {
        return ConditionDeShortNameEnum.REAL_IP.getValue();
    }

    @Override
    protected Function<FlowRuleCoreExtInputDTO, Collection<String>> singleCustomizeValueGetter() {
        return this::getRealIp;
    }

    private Collection<String> getRealIp(FlowRuleCoreExtInputDTO ruleConditionDTO){
        if (StringUtil.isNotEmpty(ruleConditionDTO.getRealIp())){
            return Arrays.asList(ruleConditionDTO.getRealIp().split(";"));
        }
        return ListUtil.emptyList();
    }
}

5、工厂类:CliFlowRuleMatcherFactory

java 复制代码
@Component
public class CliFlowRuleMatcherFactory {

    private final Map<Long, CliFlowRuleMatcher> execDefinitionMatcherMap = new HashMap<>();

    @Autowired(required = false)
    public CliFlowRuleMatcherFactory(List<CliFlowRuleMatcher> execDefinitionMatchers) {
        if (CollectionUtil.isNotEmpty(execDefinitionMatchers)) {
            execDefinitionMatchers.forEach(execDefinitionMatcher ->
                execDefinitionMatcher.getConditionDeShortNames().forEach(conditionDeShortName ->
                    execDefinitionMatcherMap.put(conditionDeShortName, execDefinitionMatcher)
                )
            );
        }
    }

    public CliFlowRuleMatcher getByConditionDeShortName(Long conditionDeShortName) {
        return execDefinitionMatcherMap.get(conditionDeShortName);
    }
}

6、策略对象的持有类:xXxService

java 复制代码
private boolean matchCondition(FlowRuleCoreExtInputDTO condition, CliFlowRuleOutputDTO clinicalOrderFlowRule){
        int successCount = 0;
        // 若匹配条件不存在,等于直接匹配上
        Map<Long, List<CliFlowConditionOutputDTO>> flowConditionMap = clinicalOrderFlowRule.getClinicalOrderFlowConditionList().stream().collect(Collectors.groupingBy(CliFlowConditionOutputDTO::getConditionDeShortName));
        for (Map.Entry<Long, List<CliFlowConditionOutputDTO>> entry : flowConditionMap.entrySet()) {
            CliFlowRuleMatcher matcher = this.cliFlowRuleMatcherFactory.getByConditionDeShortName(entry.getKey());
            if (Objects.isNull(matcher)) {
                // 没有匹配器
                successCount++;
                continue;
            }
            if (matcher.isCustomMatcher()) {
                Set<String> valueSetDesc = entry.getValue().stream().map(CliFlowConditionOutputDTO::getConditionValueName).collect(Collectors.toSet());
                if (matcher.matchCustomizeValue(valueSetDesc, condition, RegularOperationCodeEnum.LIKE.getCode())) {
                    // 每个元短名配置匹配成功后加1
                    successCount++;
                }
            }else{
                Set<Long> valueSet = entry.getValue().stream().map(CliFlowConditionOutputDTO::getConditionValue).collect(Collectors.toSet());
                if (matcher.matchDefaultValue(valueSet, condition, RegularOperationCodeEnum.CONTAIN.getCode())) {
                    // 每个元短名配置匹配成功后加1
                    successCount++;
                }
            }
        }
        return successCount == flowConditionMap.size();
    }

7、工具类:MatchUtil

java 复制代码
/**
     * 配置值是否包含匹配值
     * @param configValues 配置值
     * @param matchValue 匹配值
     * @param regularOperationCode 运算符
     * @return 是否匹配
     */
    public static boolean match(Collection<Long> configValues, Long matchValue, Long regularOperationCode) {
        if (CollectionUtil.isEmpty(configValues) || Objects.isNull(matchValue) || Objects.isNull(regularOperationCode)) {
            return false;
        }

        // 等于
        if (Objects.equals(RegularOperationCodeEnum.EQUAL.getCode(), regularOperationCode)) {
            if (configValues.size() > 1) {
                return false;
            }
            return Objects.equals(configValues.iterator().next(), matchValue);
        }
        // 不等于
        if (RegularOperationCodeEnum.NOT_EQUAL.getCode().equals(regularOperationCode)) {
            if (configValues.size() > 1) {
                return false;
            }
            return !Objects.equals(configValues.iterator().next(), matchValue);
        }
        // 包含
        if (RegularOperationCodeEnum.CONTAIN.getCode().equals(regularOperationCode)) {
            return configValues.contains(matchValue);
        }
        // 不包含
        if (RegularOperationCodeEnum.NOT_CONTAIN.getCode().equals(regularOperationCode)) {
            return !configValues.contains(matchValue);
        }

        return false;
    }
相关推荐
书埋不住我12 分钟前
java第三章
java·开发语言·servlet
boy快快长大14 分钟前
将大模型生成数据存入Excel,并用增量的方式存入Excel
java·数据库·excel
孟秋与你17 分钟前
【spring】spring单例模式与锁对象作用域的分析
java·spring·单例模式
菜菜-plus21 分钟前
java 设计模式 模板方法模式
java·设计模式·模板方法模式
萨达大22 分钟前
23种设计模式-模板方法(Template Method)设计模式
java·c++·设计模式·软考·模板方法模式·软件设计师·行为型设计模式
tian-ming23 分钟前
(十八)JavaWeb后端开发案例——会话/yml/过滤器/拦截器
java·开发语言·前端
不能只会打代码27 分钟前
大学课程项目中的记忆深刻 Bug —— 一次意外的数组越界
java·github·intellij-idea·话题博客
快意咖啡~33 分钟前
java.nio.charset.MalformedInputException: Input length = 1
java·开发语言·nio
IT枫斗者1 小时前
如何解决Java EasyExcel 导出报内存溢出
java·服务器·开发语言·网络·分布式·物联网
爱编程的小生1 小时前
Easyexcel(4-模板文件)
java·excel