作者:小傅哥
暖暖的春风迎面吹,桃花多多开
小傅哥 | https://bugstack.cn 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例、源码分析等。 你用剑🗡、我用刀🔪,好的代码都很烧,望你不吝出招!
你见过这样的代码嘛?类似的呢?嗯,那么恭喜你被这个世界温柔以待!
@Test
public void test_ifelse() {
Result result = null;
if ("男".equals(policy.getSex())) {
if (policy.getAge() < 18) {
if (policy.getUserSingle()) {
result = Result.buildResult("A", "红色A");
} else {
result = Result.buildResult("B", "红色B");
}
} else if (policy.getAge() >= 18 && policy.getAge() <= 30) {
if (policy.getUserMarry()) {
result = Result.buildResult("C", "红色C");
} else {
result = Result.buildResult("D", "红色D");
}
} else if (policy.getAge() > 30) {
if (policy.getUserParenting()) {
result = Result.buildResult("E", "红色E");
} else {
result = Result.buildResult("F", "红色F");
}
}
} else if ("女".equals(policy.getSex())) {
if (policy.getAge() < 18) {
if (policy.getUserSingle()) {
result = Result.buildResult("A", "黄色A");
} else {
result = Result.buildResult("B", "黄色B");
}
} else if (policy.getAge() >= 18 && policy.getAge() <= 30) {
if (policy.getUserMarry()) {
result = Result.buildResult("C", "黄色C");
} else {
result = Result.buildResult("D", "黄色D");
}
} else if (policy.getAge() > 30) {
if (policy.getUserParenting()) {
result = Result.buildResult("E", "黄色E");
} else {
result = Result.buildResult("F", "黄色F");
}
}
}
System.out.println("决策结果(IfElse):" + result);
}
- 这就不用说了,只要会```if else```写出来还是没问题的,只不过写错不错就不一定了,毕竟一层套一层。这还算少的!
## 四、规则引擎Drools
关于规则引擎简单说呢就是,将你业务逻辑中那些行为规则流程变化的部分,分离出来。交给单独的规则引擎进行处理。最终你只需要按照约定提供配置和入参,就可以达到规则的执行结果。
>Drools(JBoss Rules )具有一个易于访问企业策略、易于调整以及易于管理的开源业务规则引擎,符合业内标准,速度快、效率高。业务分析师或审核人员可以利用它轻松查看业务规则,从而检验是否已编码的规则执行了所需的业务规则。
上去就是一巴掌,然后在问为什么。好,先来把上面的代码用```Drools```处理下,之后再解释。
### 1. 环境配置
1. jdk1.8.0
2. idea + maven3.x
3. drools 7.32.0.Final
4. 案例源码下载,[关注公众号:bugstack虫洞栈](https://itstack.org/_media/qrcode.png?x-oss-process=style/may) 回复:```源码获取```
5. 可视化流程图解决方案;[flowdiagram.itstack.org](http://flowdiagram.itstack.org/)
### 2. 工程结构
```java
itstack-demo-drools-02
└── src
├── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── model
│ │ │ └── Policy.java
│ │ └── Result.java
│ ├── resources
│ │ ├── META-INF
│ │ │ └── kmodule.xml
│ │ └── rules
│ │ └── tree.drl
│ └── webapp
│ └── index.html
└── test
└── java
└── org.itstack.demo.test
└── ApiTest.java
- 关于案例中出现的代码,可以通过关注公众号获取:**bugstack虫洞栈**,回复关键字**<获取源码>**
- 以上是我们关于使用```Drools```规则引擎的的基本工程,规则引擎使用的方式并不复杂,只要按照约定的方式进行设置即可。
### 3. 代码讲解
>Policy.java & 定义决策属性,同时这也是Fact对象
```java
public class Policy {
private String sex; // 性别;男、女
private Integer age; // 年龄
private Boolean userSingle; // 单身;是/否
private Boolean userMarry; // 结婚;是/否
private Boolean userParenting; // 育儿;是/否
...get/set
}
>Result.java & 定义结果输出
```java
public class Result {
private String code;
private String info;
}
>META-INF/kmodule.xml & 配置文件
```xml
<?xml version="1.0" encoding="utf-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="rules">
<ksession name="all-rules"/>
</kbase>
</kmodule>
- kmodule 可以包含多个```kbase```,分别对应```drl```的规则文件
- ```kbase name="rules"```,name名称需要保证唯一
- kbase下面可以有一个或多个ksession,ksession的name属性必须设置,且必须唯一
- kbase的default属性,表示当前KieBase是不是默认的,如果是默认的则不用名称就可以查找到该KieBase,但每个module最多只能有一个默认KieBase
>rules/tree.drl & 规则文件
```xml
package rules;
import org.itstack.demo.model.Policy
import org.itstack.demo.Result;
global org.itstack.demo.Result res;
rule "红A"
when
Policy(sex == "男", age < 18, userSingle)
then
res.setResult("A","红色A");
end
rule "红B"
when
Policy(sex == "男", age < 18, !userSingle)
then
res.setResult("B","红色B");
end
rule "红C"
when
Policy(sex == "男", age >= 18, age <= 30, userMarry)
then
res.setResult("C","红色C");
end
rule "红D"
when
Policy(sex == "男", age >= 18, age <= 30, !userMarry)
then
res.setResult("D","红色D");
end
rule "红E"
when
Policy(sex == "男", age > 30, userParenting)
then
res.setResult("E","红色E");
end
rule "红F"
when
Policy(sex == "男", age > 30, !userParenting)
then
res.setResult("F","红色F");
end
rule "黄A"
when
Policy(sex == "女", age < 18, userSingle)
then
res.setResult("A","黄色A");
end
rule "黄B"
when
Policy(sex == "女", age < 18, !userSingle)
then
res.setResult("B","黄色B");
end
rule "黄C"
when
Policy(sex == "女", age >= 18, age <= 30, userMarry)
then
res.setResult("C","黄色C");
end
rule "黄D"
when
Policy(sex == "女", age >= 18, age <= 30, !userMarry)
then
res.setResult("D","黄色D");
end
rule "黄E"
when
Policy(sex == "女", age > 30, userParenting)
then
res.setResult("E","黄色E");
end
rule "黄F"
when
Policy(sex == "女", age > 30, !userParenting)
then
res.setResult("F","黄色F");
end
- rule 规则名称、when then end 一套组合拳,什么条件下输出什么结果
- ```sex == "女", age > 30, !userParenting```,英文逗号隔开的是and的条件,相当你的且。当不完全是,因为在后续处理中,逗号的处理逻辑在drools是有优化的。
- then中处理结果,将结果信息返回,这个结果使用是我们设置的一个```global```全局引入。最后结尾end关键字。
- 也许你会觉得这不是很像你的```if else```吗。但千万不要这么觉得,因为这只是冰山一角。而且我们前面截图一个树形结构,而这个属性结构是可以自动化生成```DRL```规则文件的。
### 4. 测试执行
>ApiTest.java & 单元测试中会设置Drools的启动过程
```java
public class ApiTest {
private KieContainer kieContainer;
private Policy policy;
@Before
public void init() {
// 构建KieServices
KieServices kieServices = KieServices.Factory.get();
kieContainer = kieServices.getKieClasspathContainer();
policy = new Policy();
policy.setSex("男");
policy.setAge(16);
policy.setUserSingle(false);
policy.setUserMarry(false);
policy.setUserParenting(false);
System.out.println("决策请求:" + JSON.toJSONString(policy));
}
@Test
public void test_drools() {
KieSession kieSession = kieContainer.newKieSession("all-rules");
kieSession.insert(policy);
Result result = new Result();
kieSession.setGlobal("res", result);
int count = kieSession.fireAllRules();
System.out.println("Fire rule(s):" + count);
System.out.println("决策结果(Drools):" + result);
kieSession.dispose();
}
}
**init() 初始化**
1. 在初始化方法中,构建```KieServices.Factory.get();```,这个过程是比较耗费资源,实际业务使用中也不会频繁的构建。
2. 从```KieServices```中获取```KieContainer```,用于给定KieModule的所有kiebase的容器。
3. 设置FACT对象,其实就是你的决策对象的一些条件值。
**test_drools() 执行规则**
1. 获取kmodule.xml中配置中名称为all-rules的session,默认为有状态的。
2. 设置决策对象```kieSession.insert(policy);```
3. 设置全局对象``` kieSession.setGlobal("res", result);```,用于最终把结果输出
4. 开始执行规则```kieSession.fireAllRules()```
5. 最终输出结果,到最后释放资源```kieSession.dispose()```
**测试结果**
```java
决策请求:{"age":16,"sex":"男","userMarry":false,"userParenting":false,"userSingle":false}
Fire rule(s):1
决策结果(Drools):B|红色B
Drools 是用 Java 语言编写的开放源码规则引擎,使用 Rete 算法对所编写的规则求值。Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,从而便于学习和理解。并且,还可以将 Java 代码直接嵌入到规则文件中,这令 Drools 的学习更加吸引人。
好!那么这样你就知道,Drools的核心内容是关于 Rete 算法的实现。接下来我们再来了解下 Rete。
为了解决生产式推理引擎效率底下的问题,Forgy 在1979年提出 Rete 算法,作为生产式系统的高效模式匹配算法。Rete 算法的初衷是:利用规则之间各个域的公用部分减少规则存储,同时保存匹配过程的临时结果以加快匹配速度。为了达到这种效果,算法将规则拆分,其中每个条件单元作为基本单位(节点)连接成一个数据辨别网络,然后将事实经过网络筛选并传播,最终所有条件都有事实匹配的规则被激活。
Rete 算法自从 1979 年提出以来,已经经历过各种改进与推广。除了对自身规则网络结构的优化外,对一些功能扩展如模糊推理、事件推理、并行化等也有很多研究。
逻辑操作符(operators)是指注入and、or、not等,的逻辑运算符处理。
规则前件顺序是指规则体哦啊见中的各个约束的排列顺序,它决定了条件链接操作的执行顺序,影响中间结果的大小,是决定规则匹配效率的关键因素。
索引方法是指对 Rete 网络的节点建立当前节点对后继 的索引,在事实断言时可以通过索引快速找到对应的后继节 点而无需逐个查找。
除了数据瑕疵,对于变化剧烈的数据也成为Rete算法需要解决的问题。
Rete 算法从提出至今,性能提升问题一直是研究重点。多核多处理器问世后,将推理过程分配到不同机器上并行处理成为一种常见的效率提升方法
如果你控制不住自己,就会有别人控制你
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。