Drools规则引擎入门小demo
1. 問題引出
現(xiàn)有一個(gè)在線申請(qǐng)信用卡的業(yè)務(wù)場(chǎng)景,用戶需要錄入個(gè)人信息,如下圖所示:
通過上圖可以看到,用戶錄入的個(gè)人信息包括姓名、性別、年齡、學(xué)歷、電話、所在公司、職位、月收入、是否有房、是否有車、是否有信用卡等。錄入完成后點(diǎn)擊申請(qǐng)按鈕提交即可。
用戶提交申請(qǐng)后,需要在系統(tǒng)的服務(wù)端進(jìn)行用戶信息合法性檢查(是否有資格申請(qǐng)信用卡),只有通過合法性檢查的用戶才可以成功申請(qǐng)到信用卡(注意:不同用戶有可能申請(qǐng)到的信用卡額度不同)。
檢查用戶信息合法性的規(guī)則如下:
| 1 | 檢查學(xué)歷與薪水1 | 如果申請(qǐng)人既沒房也沒車,同時(shí)學(xué)歷為大專以下,并且月薪少于5000,那么不通過 |
| 2 | 檢查學(xué)歷與薪水2 | 如果申請(qǐng)人既沒房也沒車,同時(shí)學(xué)歷為大專或本科,并且月薪少于3000,那么不通過 |
| 3 | 檢查學(xué)歷與薪水3 | 如果申請(qǐng)人既沒房也沒車,同時(shí)學(xué)歷為本科以上,并且月薪少于2000,同時(shí)之前沒有信用卡的,那么不通過 |
| 4 | 檢查申請(qǐng)人已有的信用卡數(shù)量 | 如果申請(qǐng)人現(xiàn)有的信用卡數(shù)量大于10,那么不通過 |
用戶信息合法性檢查通過后,還需要根據(jù)如下信用卡發(fā)放規(guī)則確定用戶所辦信用卡的額度:
| 1 | 規(guī)則1 | 如果申請(qǐng)人有房有車,或者月收入在20000以上,那么發(fā)放的信用卡額度為15000 |
| 2 | 規(guī)則2 | 如果申請(qǐng)人沒房沒車,但月收入在10000~20000之間,那么發(fā)放的信用卡額度為6000 |
| 3 | 規(guī)則3 | 如果申請(qǐng)人沒房沒車,月收入在10000以下,那么發(fā)放的信用卡額度為3000 |
| 4 | 規(guī)則4 | 如果申請(qǐng)人有房沒車或者沒房但有車,月收入在10000以下,那么發(fā)放的信用卡額度為5000 |
| 5 | 規(guī)則5 | 如果申請(qǐng)人有房沒車或者是沒房但有車,月收入在10000~20000之間,那么發(fā)放的信用卡額度為8000 |
思考:如何實(shí)現(xiàn)上面的業(yè)務(wù)邏輯呢?
我們最容易想到的就是使用分支判斷(if else)來實(shí)現(xiàn),例如通過如下代碼來檢查用戶信息合法性:
//此處為偽代碼//檢查用戶信息合法性,返回true表示檢查通過,返回false表示檢查不通過 public boolean checkUser(User user){//如果申請(qǐng)人既沒房也沒車,同時(shí)學(xué)歷為大專以下,并且月薪少于5000,那么不通過if(user.getHouse() == null && user.getcar() == null && user.getEducation().equals("大專以下") && user.getSalary < 5000){return false;}//如果申請(qǐng)人既沒房也沒車,同時(shí)學(xué)歷為大專或本科,并且月薪少于3000,那么不通過else if(user.getHouse() == null && user.getcar() == null && user.getEducation().equals("大專或本科") && user.getSalary < 3000){return false;}//如果申請(qǐng)人既沒房也沒車,同時(shí)學(xué)歷為本科以上,并且月薪少于2000,同時(shí)之前沒有信用卡的,那么不通過else if(user.getHouse() == null && user.getcar() == null && user.getEducation().equals("本科以上") && user.getSalary < 2000 && user.getHasCreditCard() == false){return false;}//如果申請(qǐng)人現(xiàn)有的信用卡數(shù)量大于10,那么不通過else if(user.getCreditCardCount() > 10){return false;}return true; }如果用戶信息合法性檢查通過后,還需要通過如下代碼確定用戶所辦信用卡的額度:
//此處為偽代碼//根據(jù)用戶輸入信息確定信用卡額度 public Integer determineCreditCardLimit(User user){//如果申請(qǐng)人有房有車,或者月收入在20000以上,那么發(fā)放的信用卡額度為15000if((user.getHouse() != null && user.getcar() != null) || user.getSalary() > 20000){return 15000;}//如果申請(qǐng)人沒房沒車,并且月收入在10000到20000之間,那么發(fā)放的信用卡額度為6000else if(user.getHouse() == null && user.getcar() == null&& user.getSalary() > 10000 && user.getSalary() < 20000){return 6000;}//如果申請(qǐng)人沒房沒車,并且月收入在10000以下,那么發(fā)放的信用卡額度為3000else if(user.getHouse() == null && user.getcar() == null&& user.getSalary() < 10000){return 3000;}//如果申請(qǐng)人有房沒車或者沒房但有車,并且月收入在10000以下,那么發(fā)放的信用卡額度為5000else if((((user.getHouse() != null && user.getcar() == null) || (user.getHouse() == null && user.getcar() != null))&& user.getSalary() < 10000){return 5000;}//如果申請(qǐng)人有房沒車或者沒房但有車,并且月收入在10000到20000之間,那么發(fā)放的信用卡額度為8000else if((((user.getHouse() != null && user.getcar() == null) || (user.getHouse() == null && user.getcar() != null))&& (user.getSalary() > 10000 && user.getSalary() < 20000)){return 8000;} }通過上面的偽代碼我們可以看到,我們的業(yè)務(wù)規(guī)則是通過Java代碼的方式實(shí)現(xiàn)的。這種實(shí)現(xiàn)方式存在如下問題:
1、硬編碼實(shí)現(xiàn)業(yè)務(wù)規(guī)則難以維護(hù)
2、硬編碼實(shí)現(xiàn)業(yè)務(wù)規(guī)則難以應(yīng)對(duì)變化
3、業(yè)務(wù)規(guī)則發(fā)生變化需要修改代碼,重啟服務(wù)后才能生效
那么面對(duì)上面的業(yè)務(wù)場(chǎng)景,還有什么好的實(shí)現(xiàn)方式嗎?
答案是規(guī)則引擎。
2. 規(guī)則引擎概述
2.1 什么是規(guī)則引擎
規(guī)則引擎,全稱為業(yè)務(wù)規(guī)則管理系統(tǒng),英文名為BRMS(即Business Rule Management System)。規(guī)則引擎的主要思想是將應(yīng)用程序中的業(yè)務(wù)決策部分分離出來,并使用預(yù)定義的語義模塊編寫業(yè)務(wù)決策(業(yè)務(wù)規(guī)則),由用戶或開發(fā)者在需要時(shí)進(jìn)行配置、管理。
需要注意的是規(guī)則引擎并不是一個(gè)具體的技術(shù)框架,而是指的一類系統(tǒng),即業(yè)務(wù)規(guī)則管理系統(tǒng)。目前市面上具體的規(guī)則引擎產(chǎn)品有:drools、VisualRules、iLog等。
規(guī)則引擎實(shí)現(xiàn)了將業(yè)務(wù)決策從應(yīng)用程序代碼中分離出來,接收數(shù)據(jù)輸入,解釋業(yè)務(wù)規(guī)則,并根據(jù)業(yè)務(wù)規(guī)則做出業(yè)務(wù)決策。規(guī)則引擎其實(shí)就是一個(gè)輸入輸出平臺(tái)。
上面的申請(qǐng)信用卡業(yè)務(wù)場(chǎng)景使用規(guī)則引擎后效果如下:
系統(tǒng)中引入規(guī)則引擎后,業(yè)務(wù)規(guī)則不再以程序代碼的形式駐留在系統(tǒng)中,取而代之的是處理規(guī)則的規(guī)則引擎,業(yè)務(wù)規(guī)則存儲(chǔ)在規(guī)則庫中,完全獨(dú)立于程序。業(yè)務(wù)人員可以像管理數(shù)據(jù)一樣對(duì)業(yè)務(wù)規(guī)則進(jìn)行管理,比如查詢、添加、更新、統(tǒng)計(jì)、提交業(yè)務(wù)規(guī)則等。業(yè)務(wù)規(guī)則被加載到規(guī)則引擎中供應(yīng)用系統(tǒng)調(diào)用。
2.2 使用規(guī)則引擎的優(yōu)勢(shì)
使用規(guī)則引擎的優(yōu)勢(shì)如下:
1、業(yè)務(wù)規(guī)則與系統(tǒng)代碼分離,實(shí)現(xiàn)業(yè)務(wù)規(guī)則的集中管理
2、在不重啟服務(wù)的情況下可隨時(shí)對(duì)業(yè)務(wù)規(guī)則進(jìn)行擴(kuò)展和維護(hù)
3、可以動(dòng)態(tài)修改業(yè)務(wù)規(guī)則,從而快速響應(yīng)需求變更
4、規(guī)則引擎是相對(duì)獨(dú)立的,只關(guān)心業(yè)務(wù)規(guī)則,使得業(yè)務(wù)分析人員也可以參與編輯、維護(hù)系統(tǒng)的業(yè)務(wù)規(guī)則
5、減少了硬編碼業(yè)務(wù)規(guī)則的成本和風(fēng)險(xiǎn)
6、使用規(guī)則引擎提供的規(guī)則編輯工具,使復(fù)雜的業(yè)務(wù)規(guī)則實(shí)現(xiàn)變得的簡(jiǎn)單
2.3 規(guī)則引擎應(yīng)用場(chǎng)景
對(duì)于一些存在比較復(fù)雜的業(yè)務(wù)規(guī)則并且業(yè)務(wù)規(guī)則會(huì)頻繁變動(dòng)的系統(tǒng)比較適合使用規(guī)則引擎,如下:
1、風(fēng)險(xiǎn)控制系統(tǒng)----風(fēng)險(xiǎn)貸款、風(fēng)險(xiǎn)評(píng)估
2、反欺詐項(xiàng)目----銀行貸款、征信驗(yàn)證
3、決策平臺(tái)系統(tǒng)----財(cái)務(wù)計(jì)算
4、促銷平臺(tái)系統(tǒng)----滿減、打折、加價(jià)購
2.4 Drools介紹
drools是一款由JBoss組織提供的基于Java語言開發(fā)的開源規(guī)則引擎,可以將復(fù)雜且多變的業(yè)務(wù)規(guī)則從硬編碼中解放出來,以規(guī)則腳本的形式存放在文件或特定的存儲(chǔ)介質(zhì)中(例如存放在數(shù)據(jù)庫中),使得業(yè)務(wù)規(guī)則的變更不需要修改項(xiàng)目代碼、重啟服務(wù)器就可以在線上環(huán)境立即生效。
drools官網(wǎng)地址:https://drools.org/
drools源碼下載地址:https://github.com/kiegroup/drools
在項(xiàng)目中使用drools時(shí),即可以單獨(dú)使用也可以整合spring使用。如果單獨(dú)使用只需要導(dǎo)入如下maven坐標(biāo)即可:
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.6.0.Final</version> </dependency>如果我們使用IDEA開發(fā)drools應(yīng)用,IDEA中已經(jīng)集成了drools插件。如果使用eclipse開發(fā)drools應(yīng)用還需要單獨(dú)安裝drools插件。
drools API開發(fā)步驟如下:
3. Drools入門案例
本小節(jié)通過一個(gè)Drools入門案例來讓大家初步了解Drools的使用方式、對(duì)Drools有一個(gè)整體概念。
3.1 業(yè)務(wù)場(chǎng)景說明
業(yè)務(wù)場(chǎng)景:消費(fèi)者在圖書商城購買圖書,下單后需要在支付頁面顯示訂單優(yōu)惠后的價(jià)格。具體優(yōu)惠規(guī)則如下:
| 1 | 規(guī)則一 | 所購圖書總價(jià)在100元以下的沒有優(yōu)惠 |
| 2 | 規(guī)則二 | 所購圖書總價(jià)在100到200元的優(yōu)惠20元 |
| 3 | 規(guī)則三 | 所購圖書總價(jià)在200到300元的優(yōu)惠50元 |
| 4 | 規(guī)則四 | 所購圖書總價(jià)在300元以上的優(yōu)惠100元 |
現(xiàn)在需要根據(jù)上面的規(guī)則計(jì)算優(yōu)惠后的價(jià)格。
3.2 開發(fā)實(shí)現(xiàn)
第一步:創(chuàng)建maven工程drools_quickstart并導(dǎo)入drools相關(guān)maven坐標(biāo)
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.10.0.Final</version> </dependency> <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version> </dependency>第二步:根據(jù)drools要求創(chuàng)建resources/META-INF/kmodule.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?> <kmodule xmlns="http://www.drools.org/xsd/kmodule"><!--name:指定kbase的名稱,可以任意,但是需要唯一packages:指定規(guī)則文件的目錄,需要根據(jù)實(shí)際情況填寫,否則無法加載到規(guī)則文件default:指定當(dāng)前kbase是否為默認(rèn)--><kbase name="myKbase1" packages="rules" default="true"><!--name:指定ksession名稱,可以任意,但是需要唯一default:指定當(dāng)前session是否為默認(rèn)--><ksession name="ksession-rule" default="true"/></kbase> </kmodule>注意:上面配置文件的名稱和位置都是固定寫法,不能更改
第三步:創(chuàng)建實(shí)體類Order
package com.example.demo.entity;/*** 訂單* @author admin*/ public class Order {/*** 訂單原始價(jià)格,即優(yōu)惠前價(jià)格*/private Double originalPrice;/*** 訂單真實(shí)價(jià)格,即優(yōu)惠后價(jià)格*/private Double realPrice;@Overridepublic String toString() {return "Order{" +"originalPrice=" + originalPrice +", realPrice=" + realPrice +'}';}public Double getOriginalPrice() {return originalPrice;}public void setOriginalPrice(Double originalPrice) {this.originalPrice = originalPrice;}public Double getRealPrice() {return realPrice;}public void setRealPrice(Double realPrice) {this.realPrice = realPrice;} }第四步:創(chuàng)建規(guī)則文件resources/rules/bookDiscount.drl (drl文件名稱可以隨意)
//圖書優(yōu)惠規(guī)則 package book.discount import com.example.demo.entity.Order//規(guī)則一:所購圖書總價(jià)在100元以下的沒有優(yōu)惠 rule "book_discount_1"when$order:Order(originalPrice < 100)then$order.setRealPrice($order.getOriginalPrice());System.out.println("成功匹配到規(guī)則一:所購圖書總價(jià)在100元以下的沒有優(yōu)惠"); end//規(guī)則二:所購圖書總價(jià)在100到200元的優(yōu)惠20元 rule "book_discount_2"when$order:Order(originalPrice < 200 && originalPrice >= 100)then$order.setRealPrice($order.getOriginalPrice() - 20);System.out.println("成功匹配到規(guī)則二:所購圖書總價(jià)在100到200元的優(yōu)惠20元"); end//規(guī)則三:所購圖書總價(jià)在200到300元的優(yōu)惠50元 rule "book_discount_3"when$order:Order(originalPrice <= 300 && originalPrice >= 200)then$order.setRealPrice($order.getOriginalPrice() - 50);System.out.println("成功匹配到規(guī)則三:所購圖書總價(jià)在200到300元的優(yōu)惠50元"); end//規(guī)則四:所購圖書總價(jià)在300元以上的優(yōu)惠100元 rule "book_discount_4"when$order:Order(originalPrice >= 300)then$order.setRealPrice($order.getOriginalPrice() - 100);System.out.println("成功匹配到規(guī)則四:所購圖書總價(jià)在300元以上的優(yōu)惠100元"); end第五步:編寫單元測(cè)試
package com.example.demo.test;import com.example.demo.entity.Order; import org.kie.api.KieServices; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession;public class Test {@org.junit.Testpublic void test1(){KieServices kieServices = KieServices.Factory.get();KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();//會(huì)話對(duì)象,用于和規(guī)則引擎交互(這里的參數(shù)取得是xml中配置的name,否則引擎不生效)KieSession kieSession = kieClasspathContainer.newKieSession("ksession-rule");//構(gòu)造訂單對(duì)象,設(shè)置原始價(jià)格,由規(guī)則引擎根據(jù)優(yōu)惠規(guī)則計(jì)算優(yōu)惠后的價(jià)格Order order = new Order();order.setOriginalPrice(210D);//將數(shù)據(jù)提供給規(guī)則引擎,規(guī)則引擎會(huì)根據(jù)提供的數(shù)據(jù)進(jìn)行規(guī)則匹配kieSession.insert(order);//激活規(guī)則引擎,如果規(guī)則匹配成功則執(zhí)行規(guī)則kieSession.fireAllRules();//關(guān)閉會(huì)話kieSession.dispose();System.out.println("優(yōu)惠前原始價(jià)格:" + order.getOriginalPrice() +",優(yōu)惠后價(jià)格:" + order.getRealPrice());}}通過上面的入門案例我們可以發(fā)現(xiàn),使用drools規(guī)則引擎主要工作就是編寫規(guī)則文件,在規(guī)則文件中定義跟業(yè)務(wù)相關(guān)的業(yè)務(wù)規(guī)則,例如本案例定義的就是圖書優(yōu)惠規(guī)則。規(guī)則定義好后就需要調(diào)用drools提供的API將數(shù)據(jù)提供給規(guī)則引擎進(jìn)行規(guī)則模式匹配,規(guī)則引擎會(huì)執(zhí)行匹配成功的規(guī)則并將計(jì)算的結(jié)果返回給我們。
可能大家會(huì)有疑問,就是我們雖然沒有在代碼中編寫規(guī)則的判斷邏輯,但是我們還是在規(guī)則文件中編寫了業(yè)務(wù)規(guī)則,這跟在代碼中編寫規(guī)則有什么本質(zhì)的區(qū)別呢?
我們前面其實(shí)已經(jīng)提到,使用規(guī)則引擎時(shí)業(yè)務(wù)規(guī)則可以做到動(dòng)態(tài)管理。業(yè)務(wù)人員可以像管理數(shù)據(jù)一樣對(duì)業(yè)務(wù)規(guī)則進(jìn)行管理,比如查詢、添加、更新、統(tǒng)計(jì)、提交業(yè)務(wù)規(guī)則等。這樣就可以做到在不重啟服務(wù)的情況下調(diào)整業(yè)務(wù)規(guī)則。
3.3 小結(jié)
3.3.1 規(guī)則引擎構(gòu)成
drools規(guī)則引擎由以下三部分構(gòu)成:
- Working Memory(工作內(nèi)存)
- Rule Base(規(guī)則庫)
- Inference Engine(推理引擎)
其中Inference Engine(推理引擎)又包括:
- Pattern Matcher(匹配器)
- Agenda(議程)
- Execution Engine(執(zhí)行引擎)
3.3.2 相關(guān)概念說明
Working Memory:工作內(nèi)存,drools規(guī)則引擎會(huì)從Working Memory中獲取數(shù)據(jù)并和規(guī)則文件中定義的規(guī)則進(jìn)行模式匹配,所以我們開發(fā)的應(yīng)用程序只需要將我們的數(shù)據(jù)插入到Working Memory中即可,例如本案例中我們調(diào)用kieSession.insert(order)就是將order對(duì)象插入到了工作內(nèi)存中。
Fact:事實(shí),是指在drools 規(guī)則應(yīng)用當(dāng)中,將一個(gè)普通的JavaBean插入到Working Memory后的對(duì)象就是Fact對(duì)象,例如本案例中的Order對(duì)象就屬于Fact對(duì)象。Fact對(duì)象是我們的應(yīng)用和規(guī)則引擎進(jìn)行數(shù)據(jù)交互的橋梁或通道。
Rule Base:規(guī)則庫,我們?cè)谝?guī)則文件中定義的規(guī)則都會(huì)被加載到規(guī)則庫中。
Pattern Matcher:匹配器,將Rule Base中的所有規(guī)則與Working Memory中的Fact對(duì)象進(jìn)行模式匹配,匹配成功的規(guī)則將被激活并放入Agenda中。
Agenda:議程,用于存放通過匹配器進(jìn)行模式匹配后被激活的規(guī)則。
Execution Engine:執(zhí)行引擎,執(zhí)行Agenda中被激活的規(guī)則。
總結(jié)
以上是生活随笔為你收集整理的Drools规则引擎入门小demo的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: poj 1715 Hexadecima
- 下一篇: Android LitePal使用总结