• drools规则引擎01


    drools入门-01

    一、规则引擎概述
    1. 业务场景
    1.1 需求

    商城系统,消费赠送积分,规则如下:

    100元以下,不加分

    100元~500元 加100分

    500元~1000元 加500分

    1000元以上 加1000分

    1.2 传统做法

    if…else:

    package com.example.droolstest;
    
    /**
     * @author 木子的昼夜编程
     */
    public class Test01 {
        public static void main(String[] args) {
            // 充值金额
            int amont = 100;
            // 获取原积分
            int score = 0;
            if (amont <= 100) {
                score = score + 0;
            }else if (amont >100 && amont<=500) {
                score = score + 100;
            } else if (amont > 500 && amont<= 1000){
                score = score + 500;
            } else if (amont > 1000) {
                score = score + 1000;
            }
            System.out.println(score);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    策略模式:

    讲实话,如果不用配置文件进行配置,策略模式跟if…else没有什么区别(我个人认为)

    package com.example.droolstest;
    
    /**
     * @author 木子的昼夜编程
     */
    public class Test01 {
        public static void main(String[] args) {
            // 充值金额
            int amont = 100;
            // 获取原积分
            int score = 0;
            Strategy strategy = null;
            if (amont <= 100) {
                strategy = new Strategy01();
            }else if (amont >100 && amont<=500) {
                strategy = new Strategy02();
            } else if (amont > 500 && amont<= 1000){
                strategy = new Strategy03();
            } else if (amont > 1000) {
                strategy = new Strategy04();
            }
            Context context = new Context(strategy);
            System.out.println(context.addScore(score));
        }
    
    
    }
    class Context{
        Strategy strategy;
        public Context(Strategy strategy){
            this.strategy = strategy;
        }
        int addScore(int score){
            return strategy.addScore(score);
        }
    }
    interface Strategy{
        int addScore(int score);
    }
    class Strategy01 implements Strategy{
        @Override
        public int addScore(int score) {
            return score + 0;
        }
    }
    class Strategy02 implements Strategy{
        @Override
        public int addScore(int score) {
            return score + 100;
        }
    }
    class Strategy03 implements Strategy{
        @Override
        public int addScore(int score) {
            return score + 500;
        }
    }
    class Strategy04 implements Strategy{
        @Override
        public int addScore(int score) {
            return score + 100;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    如果是一个固定的类型,我们可以用Map<>,映射一个对应关系出来,也就是把if…else用key-value实现

    但是范围的话… 我想想能不能简单化一点。

    绞尽脑汁想到了一个(其实是百度找的)好像简单了点儿,但是貌似以后修改改动不必if…else简单。

    package com.example.droolstest;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author 木子的昼夜编程
     */
    public class Test01 {
        public static void main(String[] args) {
            // 充值金额
            int amont = 340;
            // 获取原积分
            int score = 0;
            Strategy strategy = WhichStrategy.getStrategy(amont);
            Context context = new Context(strategy);
            System.out.println(context.addScore(score));
        }
    
    
    }
    class Context{
        Strategy strategy;
        public Context(Strategy strategy){
            this.strategy = strategy;
        }
        int addScore(int score){
            return strategy.addScore(score);
        }
    }
    class WhichStrategy{
        int start;
        int end;
        Strategy strategy;
        public WhichStrategy(int start, int end, Strategy strategy) {
            this.start = start;
            this.end = end;
            this.strategy = strategy;
        }
        static List<WhichStrategy> allStrategy = new ArrayList<>();
        static {
            allStrategy.add(new WhichStrategy(0,100, new Strategy01()));
            allStrategy.add(new WhichStrategy(100,500, new Strategy02()));
            allStrategy.add(new WhichStrategy(500,1000, new Strategy03()));
            allStrategy.add(new WhichStrategy(1000,Integer.MAX_VALUE, new Strategy04()));
        }
    
        public static Strategy getStrategy(int amont){
            if (amont <0 ) {
                throw new RuntimeException("闹着玩呢?充值负数?");
            }
            for (int i = 0; i < allStrategy.size(); i++) {
                WhichStrategy whichStrategy= allStrategy.get(i);
                if (whichStrategy.start < amont && whichStrategy.end >= amont) {
                    return whichStrategy.strategy;
                }
            }
            throw new RuntimeException("没有匹配到策略!");
        }
    }
    interface Strategy{
        int addScore(int score);
    }
    class Strategy01 implements Strategy{
        @Override
        public int addScore(int score) {
            return score + 0;
        }
    }
    class Strategy02 implements Strategy{
        @Override
        public int addScore(int score) {
            return score + 100;
        }
    }
    class Strategy03 implements Strategy{
        @Override
        public int addScore(int score) {
            return score + 500;
        }
    }
    class Strategy04 implements Strategy{
        @Override
        public int addScore(int score) {
            return score + 100;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    1.3 问题

    其实如果需求很固定,那就很简单了,我们if…else能解决99%的业务。

    如果需求变更呢?积分层次增加?积分比例调整?

    我们把这些范围对应的积分配置化?放数据库?放配置中心

    问题:

    我们要简化if…else,让业务和数据库逻辑分离

    我们要用更方便、更便于维护的程序来实现这些功能。而且修改了逻辑后,程序不用重启。

    2. 是什么
    2.1 概念来一波

    规则引擎由推理引擎发展而来的,是一种嵌入在应用程序的组件,实现了将业务决策从应用程序代码中分离出来(也就是新增和删除if…else的动作不再由修改代码实现,可以由配置实现),并使用预定的语义模块编写业务决策。接收数据输入,解释业务规则,并根据业务规则作出业务决策。

    注意:规则引擎不是某一个具体的技术框架,而是只一类系统,即业务规则管理系统。目前市面上具体的规则引擎产品有:drools、visualRules、iLog、Easy Ruls、RuleBook、Openl-Tablets等

    我们一般在营销系统、crm系统、库存、风控、采购、促销平台等用的比较多。

    比如贷款发放、票价、折扣(打折、满减、加价购等)、价格、风控规则等各种业务规则。

    2.2 原理-基于rete算法的规则引擎
    2.2.1 原理

    在AI领域,产生式系统是一个很重要的理论,产生式推理分为正向推理和逆向推理产生式,其规则的一般形式是:

    If条件Then操作。rete算法是实现产生式系统中正向推理的高效模式匹配算法,通过形成一个rete网络进行模式匹配,利用基于规则的系统的时间冗余性和结构相似性特征,提高系统模式匹配效率。

    正向推理:

    正向推理也叫演绎法,我们经常看军旅电视剧和刑侦电视剧都有推演这么一说,就是通过已经发生的事情,想想后边的事情,比如杀人犯A,连续3年在中秋节杀人,那我们有理由推理出,今年中秋节,他还会出来杀人。

    正向推理由事实驱动,从一个初始的事实出发,不断地应用规则得出结论。首先在候选队列中选择一条规则作为启用规则进行推理,记录其结论作为下一步推理的证据。如果重复这个过程,直到再无可用规则可被选用或者得到了所要求的解位置。

    反向推理:

    反向推理也叫归纳法,我们假设今年中秋会出现月饼涨价,我们需要支撑这个推论的证据是去年、前年、大前年、大大前年连续4年都是中秋月饼涨价,然后我们搜集证据,发现确实如此,那我们就可以证明,今年月饼会涨价。

    反向推理由目标驱动,首先提出某个假设,然后寻找支持该假设的证据,若所需的证据都能找到,说明原假设是正确的,若无论如何都找不到所需证据,则说明原假设不成立,此时需要另做新的假设。

    2.2.2 rete算法

    我们不深究这个算法的实际实现数学公式,我们只做简单了解即可。(当然如果有能力你去研究一下原理也是非常ok的),我们知道我们写了那么多规则文件,最后怎么快速匹配,快速执行,快速出结果的,是用到了rete算法。

    在网上看到一段话:

    rete算法最初是由卡内基美隆大学Charles L.Forgy博士在1974年发表的论文中阐述的算法(好家伙,比我还早出现将近20年),该算法提供了专家系统的一个高效实现。自rete算法提出以后,他就被用在一些大型的规则系统中。

    rete在拉丁语中被译为“net”,也就是网络的意思,rete匹配算法是一种进行大规模集合和大量对象集合间比较的高效算法,通过网络少选的方法找出所有匹配的各个模式的对象和规则。

    其核心算法是将分离的匹配项根据内容动态构造匹配树(我们听到树这个词,就应该意识到是为了加快速度的),以达到显著降低计算量的效果。rete算法可以被分为两部分:规则编译和规则执行。当rete算法进行事实的断言时,包含三个阶段:匹配、选择和执行,称做:match-select-act cycle

    3.Drools介绍

    https://github.com/kiegroup/drools

    drools具有一个易于访问企业策略、易于调整以及易于管理的开源业务规则引擎,符合业内标准,速度快、效率高。业务分析师或审核人员可以利用它轻松查看业务规则,从而检验已编码的规则是否执行了所需的业务规则。其前身是Codehaus的一个开源项目叫Drools,后被纳入JBoos门下,更名为Jboss Rules,成为了JBoos应用服务器的规则引擎。

    Drools被分为两个主要部分:编译和运行。编译是将规则描述文件按照ANTLR 3语法进行解析,对语法进行正确性检查(跟我们java代码编译一个道理),然后生成一种中间结构“descr”(可以类比我们的.class文件),descr用AST来描述规则。目前,Drools支持4中规则描述文件:drl、xls、brl和dsl文件。其中的描述文件是drl文件和xls文件,xls文件更易于维护,更直观,更容易被业务人员所理解(毕竟业务人员更喜欢操作Excel文档)。运行时是将AST传到PackageBuilder,由PackageBuilder产生rulebase,它包含了一个或多个Package对象。

    二、入门(案例、相关概更念、springboot集成)
    1. 新建spring-boot项目 引入依赖

    lombok 这个跟drools没直接关系,但是用了都说好。

    drools-core、drools-compiler 这俩是主要的jar包

    <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starterartifactId>
            dependency>
    		
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            
            <dependency>
                <groupId>org.droolsgroupId>
                <artifactId>drools-coreartifactId>
                <version>7.17.0.Finalversion>
            dependency>
            <dependency>
                <groupId>org.droolsgroupId>
                <artifactId>drools-compilerartifactId>
                <version>7.17.0.Finalversion>
            dependency>
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    2. 编写文件

    com.example.droolstest.entity.Order.java

    package com.example.droolstest.entity;
    
    import lombok.Data;
    import lombok.experimental.Accessors;
    
    
    /**
     * @author 木子的昼夜编程
     */
    @Data
    @Accessors(chain = true)
    public class Order {
        /**
         * 订单原价金额
         */
        private int price;
        /**
         *积分
         */
        private int score;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    com.example.droolstest.entity.User.java

    package com.example.droolstest.entity;
    
    import lombok.Data;
    import lombok.experimental.Accessors;
    
    /**
     * @author 木子的昼夜编程
     */
    @Data
    @Accessors(chain = true)
    public class User {
        /**
         * 姓名
         */
        private String name;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    resources/rules/order-rules.drl

    package rules;
    
    // 引入实体
    import com.example.droolstest.entity.Order;
    import com.example.droolstest.entity.User;
    
    // 编写规则
    rule "add0"
        no-loop true
        lock-on-active true
        salience 1
        when
            $order : Order(price <= 100)
        then
            $order.setScore(0);
            update($order)
    end
    
    rule "add100"
        no-loop true
        lock-on-active true
        salience 1
        when
            $order : Order(price > 100 && price <= 500)
        then
            $order.setScore(100);
            update($order)
    end
    
    rule "add500"
        no-loop true
        lock-on-active true
        salience 1
        when
            $order : Order(price > 500 && price <= 1000)
        then
            $order.setScore(500);
            update($order)
    end
    
    rule "add1000"
        no-loop true
        lock-on-active true
        salience 1
        when
            $order : Order(price > 1000)
        then
            $order.setScore(1000);
            update($order)
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    DroolsTestApplicationTests

    package com.example.droolstest;
    
    import com.example.droolstest.entity.Order;
    import com.example.droolstest.entity.User;
    import org.drools.core.impl.InternalKnowledgeBase;
    import org.drools.core.impl.KnowledgeBaseFactory;
    import org.junit.jupiter.api.Test;
    import org.kie.api.io.ResourceType;
    import org.kie.api.runtime.KieSession;
    import org.kie.internal.builder.KnowledgeBuilder;
    import org.kie.internal.builder.KnowledgeBuilderFactory;
    import org.kie.internal.io.ResourceFactory;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.List;
    
    @SpringBootTest
    class DroolsTestApplicationTests {
    
        // 测试
        @Test
        public void droolsOrderTest() throws Exception {
            KieSession kieSession = getKieSession();
            List<Order> orderList = getInitData();
            for (Order order: orderList) {
                // 1-规则引擎处理逻辑
                kieSession.insert(order);
                kieSession.fireAllRules();
                // 2-执行完规则后, 执行相关的业务逻辑 我们就简单输出
                addScore(order);
            }
            kieSession.dispose();
        }
    
        private static void addScore(Order o){
            System.out.println("用户" + o.getUser().getName() + "享受额外增加积分: " + o.getScore());
        }
    
        // 获取session 线程不安全 所以需要每次使用的时候获取新的
        public KieSession getKieSession(){
            KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
            // 这里可以从各个地方获取配置文件 比如网络 字节数组 
            kbuilder.add(ResourceFactory.newClassPathResource("rules/order-rules.drl"), ResourceType.DRL);
            //BUILD RULEBASE
            InternalKnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
            kbase.addPackages( kbuilder.getKnowledgePackages() );
            //NEW WORKING MEMORY
            final KieSession session = kbase.newKieSession();
            return session;
        }
    
        // 初始化数据(模拟数据库获取的数据)
        private static List<Order> getInitData(){
            List<Order> orderList = new ArrayList<>();
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            // 张三买东西
            {
                Order order = new Order();
                order.setPrice(80);
                User user = new User();
                user.setName("张三");
                order.setUser(user);
                order.setScore(111);
                orderList.add(order);
            }
            // 李四买东西
            {
                Order order = new Order();
                order.setPrice(200);
                User user = new User();
                user.setName("李四");
                order.setUser(user);
                orderList.add(order);
            }
            // 王五买东西
            {
                Order order = new Order();
                order.setPrice(800);
                User user = new User();
                user.setName("王五");
                order.setUser(user);
                orderList.add(order);
            }
            // 孙子买东西
            {
                Order order = new Order();
                order.setPrice(1500);
                User user = new User();
                user.setName("孙子");
                order.setUser(user);
                orderList.add(order);
            }
            return orderList;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    输出结果:

    在这里插入图片描述

  • 相关阅读:
    解决selenium使用chrome下载文件(如pdf)时,反而打开浏览器的预览界面
    微信小程序备案内容常见问题汇总
    计网--应用层
    JSP旅馆房间预订管理系统myeclipse定制开发mysql数据库网页模式java编程jdbc
    Java求解一元二次方程
    Java List 集合取 交集、并集、差集、补集 Java集合取交集、Java集合并集
    教师资格证报名浏览器不兼容 - 解决方案
    stm32f334高级定时器TIM1
    k8s docker 中部署think php 并搭建php websocket
    NDK交叉编译
  • 原文地址:https://blog.csdn.net/qq_36291682/article/details/127593083