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;
    }
相关推荐
坐吃山猪5 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫6 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao6 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区7 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT8 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy8 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss10 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续10 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben04410 小时前
ReAct模式解读
java·ai