规则引擎说起来比较简单,当你的功能有很多未知条件需要判断或需要遵循很多业务规则时,就可以采用。
首先看看规则文件大大致样子,
rule "testDrools" when ...各种条件 then ...执行业务 end
看起来比较像if else,但是切忌,我们应该用一个业务人员的视角而不是一个程序员的视角来看规则文件,否则你会进入很多误区。
当多个工作对象进入规则引擎时,它可以根据多个条件同时判断,并且执行不同的业务代码。
下面有个场景,看看如何把规则引擎应用其中。
假如现在有两组数据对象,分别是现金流数据和账户数据,我们需要根据某个账户在某段时间内的现金流动状况来给账户计算余额。
首先新建现金流对象,账户对象,时间查询对象,分别如下(省去getter setter)
public class CashFlow { private Date date; private double amount; private String type; private long accountNo; } public class Account { private long accountNo; private double blance; } public class AccountPeriod { private Date start; private Date end; }
我们的目的是根据传入的AccountPeriod对象和一系列CashFlow,Account对象来自动计算出每个Account对象的blance。
规则文件应该如下编写:
package org.drools.examples.cashflow import org.drools.myTest.cashflow.Account; import org.drools.myTest.cashflow.AccountPeriod; import org.drools.myTest.cashflow.CashFlow; rule "CREDIT" when $ap:AccountPeriod() $acc:Account($accountNo:accountNo) CashFlow(type=="CREDIT",accountNo==$accountNo,date>=$ap.start && <=$ap.end,$amount:amount) then $acc.setBlance($acc.getBlance()+$amount); System.out.println("借入"); end rule "DEBITS" when $ap:AccountPeriod() $acc:Account($accountNo:accountNo) CashFlow(type=="DEBIT",accountNo==$accountNo,date>=$ap.start && <=$ap.end,$amount:amount) then $acc.setBlance($acc.getBlance()-$amount); System.out.println("借出"); end
drl规则文件引入包的语句和java类似,重点在下面。
首先创建规则,名为“CREDIT”,when里面是条件,冒号是赋值,以便下文使用,"==" ">=" "<="等符号的使用都和java类似。所以这个条件的意思是:
当传入的对象里面有AccountPeriod(并赋值到$ap),并且存在账户信息(并把accountNo赋值到$accountNo),并且存在现金流信息(并符合各属性的值的匹配),那么执行余额的增加。
规则DEBITS的逻辑差不多。
编写引擎的编译及部署代码,如下
// KnowledgeBuilder用来在业务代码中收集已经编写好的规则,然后对这些规则文件进行编译,最终产生 // 一批编译好的规则包(KnowledgePackage)给其他的应用程序使用 final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory .newKnowledgeBuilder(); // 解析并且编译规则文件 kbuilder.add(ResourceFactory.newClassPathResource("cashflow.drl", CashFlowTestMain.class), ResourceType.DRL); // 打出错误 if (kbuilder.hasErrors()) { System.out.println(kbuilder.getErrors().toString()); throw new RuntimeException("Unable to compile \"cashflow.drl\"."); } // 得到编译后的包 final Collection<KnowledgePackage> pkgs = kbuilder .getKnowledgePackages(); // 部署到一个知识库 // KnowledgeBase是用来收集应用当中知识定义的知识库对象,在一个KnowledgeBase当中,可以包含普通的规则(rule) // 规则流(rule flow),函数定义(function),用户自定义对象(type model), final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); kbase.addKnowledgePackages(pkgs);
规则引擎相当于一个容器,我们需要通过一个管道跟这个容器连起来,这个管道就是会话,新建一个有状态的会话:
//开启有状态会话 final StatefulKnowledgeSession ksession = kbase .newStatefulKnowledgeSession();
ksession 对象的insert方法可以把对象放入工作区(容器),让规则引擎来处理。下面新建几条测试数据:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); /** ******************第一条记录************************* */ CashFlow cf0 = new CashFlow(); String time0 = "2010-01-12"; cf0.setDate(sdf.parse(time0)); cf0.setAmount(100); cf0.setType("CREDIT"); cf0.setAccountNo(1); ksession.insert(cf0); CashFlow cf1 = new CashFlow(); String time1 = "2010-02-02"; cf1.setDate(sdf.parse(time1)); cf1.setAmount(200); cf1.setType("DEBIT"); cf1.setAccountNo(1); ksession.insert(cf1); CashFlow cf2 = new CashFlow(); String time2 = "2010-05-18"; cf2.setDate(sdf.parse(time2)); cf2.setAmount(50); cf2.setType("CREDIT"); cf2.setAccountNo(1); ksession.insert(cf2); CashFlow cf3 = new CashFlow(); String time3 = "2010-03-18"; cf3.setDate(sdf.parse(time3)); cf3.setAmount(75); cf3.setType("CREDIT"); cf3.setAccountNo(1); ksession.insert(cf3); Account acc = new Account(); acc.setAccountNo(1); acc.setBlance(0); ksession.insert(acc); /** ******************第二条记录************************* */ CashFlow cf0_else = new CashFlow(); String time0_else = "2010-01-12"; cf0_else.setDate(sdf.parse(time0_else)); cf0_else.setAmount(150); cf0_else.setType("CREDIT"); cf0_else.setAccountNo(2); ksession.insert(cf0_else); CashFlow cf1_else = new CashFlow(); String time1_else = "2010-02-02"; cf1_else.setDate(sdf.parse(time1_else)); cf1_else.setAmount(500); cf1_else.setType("DEBIT"); cf1_else.setAccountNo(2); ksession.insert(cf1_else); CashFlow cf2_else = new CashFlow(); String time2_else = "2010-05-18"; cf2_else.setDate(sdf.parse(time2_else)); cf2_else.setAmount(50); cf2_else.setType("CREDIT"); cf2_else.setAccountNo(2); ksession.insert(cf2_else); CashFlow cf3_else = new CashFlow(); String time3_else = "2010-03-18"; cf3_else.setDate(sdf.parse(time3_else)); cf3_else.setAmount(75); cf3_else.setType("CREDIT"); cf3_else.setAccountNo(2); ksession.insert(cf3_else); Account acc_else = new Account(); acc_else.setAccountNo(2); acc_else.setBlance(0); ksession.insert(acc_else); /* 查询条件 */ AccountPeriod ap = new AccountPeriod(); String start = "2009-05-06"; String end = "2012-02-25"; ap.setStart(sdf.parse(start)); ap.setEnd(sdf.parse(end)); ksession.insert(ap); // 触发所有规则执行 ksession.fireAllRules(); // 一定要释放 ksession.dispose();
至此,规则已全部启动,为了测试,打印这两个账户的余额信息:
System.out.println(acc.getAccountNo() + " : " + acc.getBlance()); System.out.println(acc_else.getAccountNo() + " : " + acc_else.getBlance());
结果为:
借出 借入 借入 借入 借出 借入 借入 借入 1 : 25.0 2 : -225.0
By 阿飞哥 转载请说明
腾讯微博: http://t.qq.com/duyunfeiRoom
新浪微博: http://weibo.com/u/1766094735