Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >多条件判断场景中规则执行器的设计

多条件判断场景中规则执行器的设计

作者头像
Erwin
发布于 2021-04-26 03:10:43
发布于 2021-04-26 03:10:43
87500
代码可运行
举报
文章被收录于专栏:啸天"s blog啸天"s blog
运行总次数:0
代码可运行

业务场景

近日在公司领到一个小需求,需要对之前已有的试用用户申请规则进行拓展。我们的场景大概如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (是否海外用户) {
 return false;
}

if (刷单用户) {
  return false;
}

if (未付费用户 && 不再服务时段) {
  return false
}

if (转介绍用户 || 付费用户 || 内推用户) {
  return true;
}
复制代码

按照上述的条件我们可以得出的结论是:

  1. 咱们的的主要流程主要是基于 and 或者 or 的关系。
  2. 如果有一个不匹配的话,其实咱们后续的流程是不用执行的,就是需要具备一个短路的功能。
  3. 对于目前的现状来说,我如果在原有的基础上来该,只要稍微注意一下解决需求不是很大的问题,但是说后面可维护性非常差。

后面进过权衡过后,我还是决定将这个部分进行重构一下。

规则执行器

针对这个需求,我首先梳理了一下咱们规则执行器大概的设计, 然后我设计了一个 V1 版本和大家一起分享一下,如果大家也有这样的 case 可以给我分享留言,下面部分主要是设计和实现的流程和 code .

规则执行器的设计

对于规则的抽象并实现规则

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 业务数据
@Data
public class RuleDto {
  private String address;
    private int age;
}

// 规则抽象
public interface BaseRule {

    boolean execute(RuleDto dto);
}

// 规则模板
public abstract class AbstractRule implements BaseRule {

    protected <T> T convert(RuleDto dto) {
        return (T) dto;
    }

    @Override
    public boolean execute(RuleDto dto) {
        return executeRule(convert(dto));
    }

    protected <T> boolean executeRule(T t) {
        return true;
    }
}

// 具体规则- 例子1
public class AddressRule extends AbstractRule {

    @Override
    public boolean execute(RuleDto dto) {
        System.out.println("AddressRule invoke!");
        if (dto.getAddress().startsWith(MATCH_ADDRESS_START)) {
            return true;
        }
        return false;
    }
}

// 具体规则- 例子2
public class NationalityRule extends AbstractRule {

    @Override
    protected <T> T convert(RuleDto dto) {
        NationalityRuleDto nationalityRuleDto = new NationalityRuleDto();
        if (dto.getAddress().startsWith(MATCH_ADDRESS_START)) {
            nationalityRuleDto.setNationality(MATCH_NATIONALITY_START);
        }
        return (T) nationalityRuleDto;
    }

    @Override
    protected <T> boolean executeRule(T t) {
        System.out.println("NationalityRule invoke!");
        NationalityRuleDto nationalityRuleDto = (NationalityRuleDto) t;
        if (nationalityRuleDto.getNationality().startsWith(MATCH_NATIONALITY_START)) {
            return true;
        }
        return false;
    }
}

// 常量定义
public class RuleConstant {
    public static final String MATCH_ADDRESS_START= "北京";
    public static final String MATCH_NATIONALITY_START= "中国";
}
复制代码

执行器构建

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class RuleService {

    private Map<Integer, List<BaseRule>> hashMap = new HashMap<>();
    private static final int AND = 1;
    private static final int OR = 0;

    public static RuleService create() {
        return new RuleService();
    }

    public RuleService and(List<BaseRule> ruleList) {
        hashMap.put(AND, ruleList);
        return this;
    }

    public RuleService or(List<BaseRule> ruleList) {
        hashMap.put(OR, ruleList);
        return this;
    }

    public boolean execute(RuleDto dto) {
        for (Map.Entry<Integer, List<BaseRule>> item : hashMap.entrySet()) {
            List<BaseRule> ruleList = item.getValue();
            switch (item.getKey()) {
                case AND:
                    // 如果是 and 关系,同步执行
                    System.out.println("execute key = " + 1);
                    if (!and(dto, ruleList)) {
                        return false;
                    }
                    break;
                case OR:
                    // 如果是 or 关系,并行执行
                    System.out.println("execute key = " + 0);
                    if (!or(dto, ruleList)) {
                        return false;
                    }
                    break;
                default:
                    break;
            }
        }
        return true;
    }

    private boolean and(RuleDto dto, List<BaseRule> ruleList) {
        for (BaseRule rule : ruleList) {
            boolean execute = rule.execute(dto);
            if (!execute) {
                // and 关系匹配失败一次,返回 false
                return false;
            }
        }
        // and 关系全部匹配成功,返回 true
        return true;
    }

    private boolean or(RuleDto dto, List<BaseRule> ruleList) {
        for (BaseRule rule : ruleList) {
            boolean execute = rule.execute(dto);
            if (execute) {
                // or 关系匹配到一个就返回 true
                return true;
            }
        }
        // or 关系一个都匹配不到就返回 false
        return false;
    }
}

复制代码

执行器的调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class RuleServiceTest {

    @org.junit.Test
    public void execute() {
        //规则执行器
        //优点:比较简单,每个规则可以独立,将规则,数据,执行器拆分出来,调用方比较规整
        //缺点:数据依赖公共传输对象 dto

        //1. 定义规则  init rule
        AgeRule ageRule = new AgeRule();
        NameRule nameRule = new NameRule();
        NationalityRule nationalityRule = new NationalityRule();
        AddressRule addressRule = new AddressRule();
        SubjectRule subjectRule = new SubjectRule();

        //2. 构造需要的数据 create dto
        RuleDto dto = new RuleDto();
        dto.setAge(5);
        dto.setName("张三");
        dto.setAddress("北京");
        dto.setSubject("数学");;

        //3. 通过以链式调用构建和执行 rule execute
        boolean ruleResult = RuleService
                .create()
                .and(Arrays.asList(nationalityRule, nameRule, addressRule))
                .or(Arrays.asList(ageRule, subjectRule))
                .execute(dto);
        System.out.println("this student rule execute result :" + ruleResult);
    }
}
复制代码

总结

规则执行器的优点和缺点

  • 优点:
    1. 比较简单,每个规则可以独立,将规则,数据,执行器拆分出来,调用方比较规整;
    2. 我在 Rule 模板类中定义 convert 方法做参数的转换这样可以能够,为特定 rule 需要的场景数据提供拓展。
  • 缺点:上下 rule 有数据依赖性,如果直接修改公共传输对象 dto 这样设计不是很合理,建议提前构建数据。

作者:乌塔卡 链接:https://juejin.cn/post/6951764927958745124 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
今天,我要干掉 if ... else ...
近日在公司领到一个小需求,需要对之前已有的试用用户申请规则进行拓展。我们的场景大概如下所示:
没有故事的陈师傅
2021/04/26
5770
今天,我要干掉 if ... else ...
抛弃繁杂的if判断,使用它试一试!
近日在公司领到一个小需求,需要对之前已有的试用用户申请规则进行拓展。我们的场景大概如下所示:
Java技术精选
2021/09/15
3000
Java规则引擎drools:drt动态生成规则并附上具体项目逻辑
由于本人的码云太多太乱了,于是决定一个一个的整合到一个springboot项目里面。
ydymz
2018/09/05
5.4K0
一文搞懂Executor执行器和线程池的关系,整体介绍其任务执行/调度体系:ThreadPoolExecutor、ScheduledExecutorService
本文进行JavaSE基础内容:Executor执行器体系的整体介绍。该文是整体框架介绍,并非局限于某一个使用的细节。由于我不止一次的被咨询说ExecutorService和ScheduledExecutorService什么区别和联系,以及ThreadPoolExecutor和ThreadPoolTaskExecutor有什么不一样之类的问题,因此决定写此文科普一下。
YourBatman
2020/04/08
3K0
一文搞懂Executor执行器和线程池的关系,整体介绍其任务执行/调度体系:ThreadPoolExecutor、ScheduledExecutorService
徒手撸了一个API网关,理解更透彻了,代码已上传github,自取~
来源 | cnblogs.com/2YSP/p/14223892.html 一、背景 最近在github上看了soul网关的设计,突然就来了兴趣准备自己从零开始写一个高性能的网关。经过两周时间的开发,
猿天地
2021/02/22
7510
徒手撸了一个API网关,理解更透彻了,代码已上传github,自取~
30个类手写Spring核心原理之自定义ORM(上)(6)
说到ResultSet,有Java开发经验的“小伙伴”自然最熟悉不过了,不过我相信对于大多数人来说也算是“最熟悉的陌生人”。从ResultSet取值操作大家都会,比如:
Tom弹架构
2021/12/16
5620
Java各种规则引擎
(2)新建配置文件/src/resources/META-INF/kmodule.xml
matinal
2020/11/27
5.3K1
Java各种规则引擎
Executor执行器与线程池
Java使用Executor框架执行多线程任务,创建与操作系统线程一对一的映射线程,由操作系统分配CPU来执行。称为任务的两级调度模型,如下图所示:
搬砖俱乐部
2019/06/15
9670
编程写判断还在用if?试试规则执行器是不是用的更顺手!
近日在公司领到一个小需求,需要对之前已有的试用用户申请规则进行拓展。我们的场景大概如下所示:
Java程序猿
2021/06/18
4230
dubbo路由代码分析3(condition路由器)
这篇说说,dubbo condition类型路由器的路由解析和执行过程 由 https://cloud.tencent.com/developer/article/1109552 这篇我们可以看到 condition类型路由器是由condition路由工厂获取,源码如下 public class ConditionRouterFactory implements RouterFactory { public static final String NAME = "condition";
技术蓝海
2018/04/26
1.5K0
dubbo路由代码分析3(condition路由器)
XXL-JOB系列二之执行器注册
如果是在基于Spring的项目中使用xxl-job,那么是由XxlJobSpringExecutor这个类来进行JobHanlder的初始化,首先这个类实现了SmartInitializingSingleton接口,这个接口的作用是在Spring容器管理的所有单例对象(非懒加载)完成实例化之后执行其回调方法afterSingletonsInstantiated, 在该方法中由initJobHanlderMethodRepository去完成初始化操作
用户9511949
2024/06/27
5340
报警系统QuickAlarm之报警执行器的设计与实现
根据前面一篇总纲的博文,将整体结构划分为了四大块,本文则主要目标集中在第一块,报警执行器(AlarmExecute)的设计与加载上了 主要的关注点无外乎 定义-》加载-》实现逻辑三块了: AlarmExecute 的接口定义 如何加载用户自定义的AlarmExecute AlarmExecute的内部实现 I. AlarmExecute接口定义 在定义接口之前,先来根据几个问题来加深下这个概念的理解: 1. 基础知识 说一下这个报警执行器到底是干嘛的? 执行具体的报警逻辑(感觉说了依据废话) 因此不同的报警
一灰灰blog
2018/03/29
7130
报警系统QuickAlarm之报警执行器的设计与实现
用建造者模式实现一个防SQL注入的ORM框架
以构建一门课程为例,一个完整的课程由PPT课件、回放视频、课堂笔记、课后作业组成,但是这些内容的设置顺序可以随意调整,我们用建造者模式来代入理解一下。首先创建一个产品类Course。
Tom弹架构
2021/10/28
1K0
Spark sql规则执行器RuleExecutor(源码解析)
Spark sql通过Analyzer中 定义的rule把Parsed Logical Plan解析成 Analyzed Logical Plan;通过Optimizer定义的rule把 Analyzed Logical Plan 优化成 Optimized Logical Plan 。
数据仓库践行者
2020/10/29
1.5K0
Spark sql规则执行器RuleExecutor(源码解析)
数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 执行
本文主要基于 Sharding-JDBC 1.5.0 正式版 1. 概述 2. ExecutorEngine 2.1 ListeningExecutorService 2.2 关闭 2.3 执行 SQL 任务 3. Executor 3.1 StatementExecutor 3.2 PreparedStatementExecutor 3.3 BatchPreparedStatementExecutor 4. ExecutionEvent 4.1 EventBus 4.2 BestEffortsDelive
芋道源码
2018/03/02
1.2K0
数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 执行
Elastic-Job系列一之执行器注册启动
以springboot为例看下elastic-job的执行器启动流程,启动配置类为elasticjob-lite-spring-boot-starter中的ElasticJobLiteAutoConfiguration,如下
用户9511949
2024/07/07
4160
mybatis源码之执行器解析 原
-从上图中可以看出所有执行器都实现了Executor接口,定义了一些通用的操作,Executor的接口定义如下
用户2603479
2019/08/31
7200
Dubbo 路由机制的实现
Dubbo 路由机制是在服务间的调用时,通过将服务提供者按照设定的路由规则来决定调用哪一个具体的服务。
ytao
2020/06/04
1.1K0
Dubbo 路由机制的实现
SpringBoot入门建站全系列(三十四)使用Drools规则引擎做排班系统
Drools 是用 Java 语言编写的开放源码规则引擎,使用 Rete 算法对所编写的规则求值。Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,从而便于学习和理解。并且,还可以将 Java 代码直接嵌入到规则文件中,这令 Drools 的学习更加吸引人。
品茗IT
2020/05/28
2.6K0
if-else 判断语句过多该如何处理?
我们平时在写代码的时候,if-else判断语句基本上必不可少,当我们的判断语句只有一两层的时候,类似下面这种,情况还好,基本上能接受;
Java极客技术
2022/12/04
6260
推荐阅读
相关推荐
今天,我要干掉 if ... else ...
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档