目录
1. 排班中遇到的烦恼
2. 规则引擎介绍
3. 解决排班中的烦恼
4. drools应用扩展
排班中遇到的烦恼
排班最原始的需求是,A一种规则,B三种规则,C一种规则。实现的方式是,先拿excel将排班的各种需求画出来。然后使用java代码实现。代码如下:
if(type.equals("A")){
复杂的代码逻辑
此处省略一百行
}else if(type.equals("B1")){
复杂的代码逻辑
此处省略一百行
}else if(type.equals("B2")){
复杂的代码逻辑
此处省略一百行
}else if(type.equals("B3")){
复杂的代码逻辑
此处省略一百行
}else if(type.equals("C")){
复杂的代码逻辑
此处省略一百行
}
过了一段时间,需求变了,要求每周休息的日期不一样。绞尽脑汁,实现了我们每周休息日期不一样的需求。
又过了一段时间,需求又变了,要求A又多了一种排班,以前是早中晚三班倒,现在是早晚两班倒。好吧,继续按照需求修改。
又过了一段时间,需求继续改动。。。
造成的结果是,代码越来愈复杂,修改以及维护起来越来越麻烦。因此,有必要找到一种技术可以将排班规则和代码解耦,不管规则如何变化,执行端不用动。
规则引擎
相关介绍
规则引擎是一种嵌套在应用程序种的组件,它实现了将业务规则从应用程序代码中分离出来,
使复杂的业务规则实现变得简单,也可以动态修改业务规则,从而快速的响应需求变更。
java开源的规则引擎有:Drools、Easy Rules、Mandarax、IBM ILOG。使用最为广泛并且开源的是Drools。
Drools 介绍
Drools 是一个基于Charles Forgy's的RETE算法的,易于访问企业策略、易于调整以及易于管理的开源业务规则引擎,符合业内标准,速度快、效率高。
业务分析师人员或审核人员可以利用它轻松查看业务规则,从而检验是否已编码的规则执行了所需的业务规则。
Drools 是用Java语言编写的开放源码规则引擎,使用Rete算法对所编写的规则求值。Drools允许使用声明方式表达业务逻辑。可以使用非XML的本地语言编写规则,从而便于学习和理解。并且,还可以将Java代码直接嵌入到规则文件中,这令Drools的学习更加吸引人。
Drools 优点
- 简化系统架构,优化应用
- 提高系统的可维护性和维护成本
- 方便系统的整合
- 减少编写“硬代码”业务规则的成本和风险
解决排班中的问题
drools有专门的规则语法drl,就是专门描述排班规则是如何执行的,按照A排班规则需求规则如下:
arrangeA.drl 文件内容
import com.hit.eryi.brms.EmployeeDto;
function String pattern1(){
String str = "{\"w1\":0,\"w2\":4 ,\"w3\":0 ,\"w4\":2 ,\"w5\":2 ,\"w6\":2,\"w7\":4 }";
return str;
}
function String pattern2(){
String str = "{\"w1\":4,\"w2\":0 ,\"w3\":4 ,\"w4\":0 ,\"w5\":2 ,\"w6\":2,\"w7\":2 }";
return str;
}
function String pattern3(){
String str = "{\"w1\":0,\"w2\":2 ,\"w3\":2 ,\"w4\":4 ,\"w5\":0 ,\"w6\":4,\"w7\":4 }";
return str;
}
function String pattern4(){
String str = "{\"w1\":2,\"w2\":0 ,\"w3\":2 ,\"w4\":0 ,\"w5\":4 ,\"w6\":4,\"w7\":4 }";
return str;
}
function String pattern5(){
String str = "{\"w1\":2,\"w2\":4 ,\"w3\":0 ,\"w4\":4 ,\"w5\":0 ,\"w6\":2,\"w7\":2 }";
return str;
}
rule "g1-01"
when
$p : EmployeeDto((order%5) == 1);
then
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
stringBuffer.append(pattern1()).append(",");
stringBuffer.append(pattern2()).append(",");
stringBuffer.append(pattern3()).append(",");
stringBuffer.append(pattern4()).append(",");
stringBuffer.append(pattern1()).append(",");
stringBuffer.append(pattern1());
stringBuffer.append("]");
$p.setArrangeJson(stringBuffer.toString());
end
rule "g1-02"
when
$p : EmployeeDto((order%5) == 2);
then
//$p.setArrangeJson("["+ pattern2() +","+ pattern3()+"," +pattern4()+","+ pattern5()+","+ pattern1() +","+ pattern2()+"]" );
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
stringBuffer.append(pattern2()).append(",");
stringBuffer.append(pattern3()).append(",");
stringBuffer.append(pattern4()).append(",");
stringBuffer.append(pattern5()).append(",");
stringBuffer.append(pattern2()).append(",");
stringBuffer.append(pattern2());
stringBuffer.append("]");
$p.setArrangeJson(stringBuffer.toString());
end
rule "g1-03"
when
$p : EmployeeDto( (order%5) == 3);
then
//$p.setArrangeJson("["+ pattern3() +","+ pattern4()+"," +pattern5()+","+ pattern1()+","+ pattern2() +","+ pattern3()+"]" );
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
stringBuffer.append(pattern3()).append(",");
stringBuffer.append(pattern4()).append(",");
stringBuffer.append(pattern5()).append(",");
stringBuffer.append(pattern1()).append(",");
stringBuffer.append(pattern3()).append(",");
stringBuffer.append(pattern3());
stringBuffer.append("]");
$p.setArrangeJson(stringBuffer.toString());
end
rule "g1-04"
when
$p : EmployeeDto( (order%5) == 4);
then
//$p.setArrangeJson("["+ pattern4() +","+ pattern5()+"," +pattern1()+","+ pattern2()+","+ pattern3() +","+ pattern4()+"]" );
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
stringBuffer.append(pattern4()).append(",");
stringBuffer.append(pattern5()).append(",");
stringBuffer.append(pattern1()).append(",");
stringBuffer.append(pattern2()).append(",");
stringBuffer.append(pattern4()).append(",");
stringBuffer.append(pattern4());
stringBuffer.append("]");
$p.setArrangeJson(stringBuffer.toString());
end
rule "g1-05"
when
$p : EmployeeDto( (order%5) == 0);
then
//$p.setArrangeJson("["+ pattern5() +","+ pattern1()+"," +pattern2()+","+ pattern3()+","+ pattern4() +","+ pattern5()+"]" );
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
stringBuffer.append(pattern5()).append(",");
stringBuffer.append(pattern1()).append(",");
stringBuffer.append(pattern2()).append(",");
stringBuffer.append(pattern3()).append(",");
stringBuffer.append(pattern5()).append(",");
stringBuffer.append(pattern5());
stringBuffer.append("]");
$p.setArrangeJson(stringBuffer.toString());
end
- package 与Java语言类似,drl的头部需要有package和import的声明,package不必和物理路径一致。
- import 导出java Bean的完整路径,也可以将Java静态方法导入调用。
- rule 规则名称,需要保持唯一 件,可以无限次执行。
- no-loop 定义当前的规则是否不允许多次循环执行,默认是 false,也就是当前的规则只要满足条件,可以无限次执行。
- lock-on-active 将lock-on-active属性的值设置为true,可避免因某些Fact对象被修改而使已经执行过的规则再次被激活执行。
- salience 用来设置规则执行的优先级,salience 属性的值是一个数字,数字越大执行优先级越高, 同时它的值可以是一个负数。默认情况下,规则的 salience 默认值为 0。如果不设置规则的 salience 属性,那么执行顺序是随机的。
- when 条件语句,就是当到达什么条件的时候
- then 根据条件的结果,来执行什么动作
- end 规则结束
Drools 其他用法
- 定时器
package com.sample;
import java.text.SimpleDateFormat;
import java.util.Date;
global java.text.SimpleDateFormat fmt;
rule "timer"
// 定时器的两种类别
// 1. java.util.Timer
// 1. 定时执行
// 2. 重复执行
// 2. crontab
// linux上crontab形式的定时
// 也同时支持定时和重复两种
// Timer
// 1. 定时执行
// timer(int: delay)
// 关键字就是timer,但是不会有粗体提示
// 同java.util.Timer,单个参数表示的是延时时间,延时指定时间之后再执行程序,从而达到延时的目的
// 注意关键字 int,还有冒号
// 2. 重复执行
// timer(int: delay interval)
// 还是java.util.Timer的用法
// 框架还是不变,第一个参数表示延时时间, 第二个表示重复执行间隔
// crontab
// 1. 基本结构
// timer(cron: * * * * * ?)
// 1. 主关键字一致,类型通过关键字区别 cron/int
// 2. 参数类型和linux的crontab任务写法一致,末尾多了个问号
// 2. 参数解释
// 0. 对应
// 秒 分 时 天 月
// 1. 定时执行
// 比如:三月二十八日二十三点四十五分五十八秒 ==> 58 45 23 28 3
// 到了指定时间,就会自动执行任务
// ps: 多点定时 三点和五点和八点整 ==> 0 0 3,5,8 * *
// 2. 周期执行
// 一个除就能够表示"每",单位当然就是看所占的位置了
// 每二十秒: */20 * * * *
// 3. 对比
// 1. 缺点
// 和上面的定时器相比,缺点在于没有"缓冲",到了时间必定执行
// 2. 有点
// 1. 准时,直接定时,而不是靠延时定时
// 2. 用法多样,功能强大
// 4. 扩展
// 1. 开关和周期
// 定时器的所谓延时和周期重复是完全独立的,但是crontab锁标注的时间,是相互影响的
// 把定点当做总开关,把周期当做细粒度控制开关,能够更深入的理解和利用crontab
// 2. 例子
// 三点五点八点时候每五分钟 * */5 3,5,8 * * *
// 每秒报时
// Timer:支持单位指定 s m h d
// timer(int: 1s 1s)
// cron:别忘了问号,linux可没有
timer(cron: */1 * * * * ?)
when
eval(true)
then
System.out.println(fmt.format(new Date()));
end
- 函数(Function)
function提供了一种在规则源文件中插入语义代码的方式,与在普通Java类中不同。他们需要帮助类,否则不能做任何事情。(实际上,编译器会针对这些句子自动产生帮助类。)在规则中使用函数的最主要优点就是你可以把所有逻辑放在一个地方,你可以根据需要更改这些函数的逻辑。函数通常用于在规则的then部分调用某些动作,特别是一些经常被用到的而传入参数不一样的动作。
典型的function格式如下:
function String hello(String name) {
return "Hello "+name+"!";
}
使用函数:
import function my.package.Foo.hello
rule "using a static function"
when
eval( true )
then
System.out.println( hello( "Bob" ) );
end
workbench简介
workbench是drools规则系统的一个系统管理平台,其中包含了一个微版的版本控制器git,一个内置版本的maven,集成了,项目创建,维护,测试,打包,发布一整套流程。
Drools使用场景
规则引擎虽然非常强大,但并非所有场景都适用。一般来说,规则引擎适用的项目都具有以下一个或多个特征:
- 需要定义了一个非常复杂的场景,甚至对于业务专家都很难完全定义
- 没有已知的或定义明确的算法解决方案
- 有挥发性的要求和需要经常更新
- 需要快速做出决定,通常是基于部分数据
具体用例
- 排班
- 订单规则、促销-折扣
- 风控
- 个人所得税
- XLS决策表,支持excel