這種場景你還寫ifelse你跟孩子坐一桌去吧
一、前言
你見過這樣的程式碼嘛?類似的呢?嗯,那麼恭喜你被這個世界溫柔以待!
if else
,並不是一個非常壞的關鍵字,只不過有人把他用壞了。尤其在接到產品需求如下這樣;
日期
需求
緊急程度
程式設計師(話外音)
星期一。早上
猿哥哥,老闆說要搞一下營銷拉拉量,給男生女生髮不同的優惠券,促活消費。
很緊急,下班就要
行吧,也不難,加下判斷就上線
星期二。下午
小哥哥,咱們上線後非常好。要讓咱們按照年輕、中年、成年,不同年齡加下判斷,準確刺激消費。
超緊急,明天就要
也不難,加就加吧
星期三。晚上
喂,小哥哥!睡了嗎!老闆說咱們這次活動很成功,可以不可以在細分下,把單身、結婚、有娃的都加上不同判斷。這樣更能刺激使用者消費。
賊緊急,最快上線。
已經意識到
ifelse
越來越多了
星期四。凌晨
哇!小哥哥你們太棒了,上的真快。嘻嘻!有個小請求,需要調整下年齡段,因為現在學生處物件的都比較早,有物件的更容易買某某某東西。要改下值!辛苦辛苦!
老闆,在等著呢!
一大片的值要修改,哎!這麼多
ifelse
了
星期五。半夜
歪歪喂!巴巴,壞了,怎麼發的優惠券不對了,有客訴了,很多女生都來投訴。你快看看。老闆,他…
(一頭汗),哎,值粘錯位置了!
終究還是一個人扛下了所有
這樣的場景你是否有遇到過呢,那麼是產品給你代溝裡去了,還是你把專案帶溝裡去了。可能會覺得,這東西這麼著急要,我也沒辦法呀。其實不止你沒有辦法,是為了打下市場,讓每一個人都很匆忙。只有合理的評估、鋪墊、架設,才會不斷滿足業務需求、產品形態的變化。否則往後的路越來越難!
二、場景
對於上面所提到的這種場景,在我們實際開發中是經常會遇到的。尤其是在一些;營銷、風控、人群等,各種使用者資訊決策樹關係時,都會出現這樣的業務邏輯。而且對於一些較大場景是肯定不會直接硬編碼
if else
,因為太難以維護。當然除非你這東西就寫一次用一次,下次不用了那無所謂。
接下來我們把上面的場景進行轉換一種樹結構圖,依次來體現出這個需求的全貌,如下;
從上圖我們看到,把產品一週提的需求彙總後就一張樹形的決策流。每一種不同的因子都可以導致結果不同的走向。
而如果這個產品整體的內容,從一點點交給你,和一整套交給你,你所做出來的研發設計是不同的。當然也有相同的,因為還有一部分很有遠見的程式設計師,他們常年踩坑!而這份相同的高等的設計,就是踩坑踩出來的經驗。
那麼,除了
if else
你還能在自己掌握的技術棧中想到什麼解決方案嗎?接下來,我們會寫出兩種實現方式,用作比對。
三、if、else編碼
@Testpublic 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。 環境配置
jdk1。8。0
idea + maven3。x
drools 7。32。0。Final
視覺化流程圖解決方案;flowdiagram。itstack。org
2。 工程結構
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
以上是我們關於使用
Drools
規則引擎的的基本工程,規則引擎使用的方式並不複雜,只要按照約定的方式進行設定即可。
3。 程式碼講解
Policy。java & 定義決策屬性,同時這也是Fact物件
public class Policy { private String sex; // 性別;男、女 private Integer age; // 年齡 private Boolean userSingle; // 單身;是/否 private Boolean userMarry; // 結婚;是/否 private Boolean userParenting; // 育兒;是/否 。。。get/set}
Result。java & 定義結果輸出
public class Result { private String code; private String info; }
META-INF/kmodule。xml & 配置檔案
<?xml version=“1。0” encoding=“utf-8” ?>
kmodule 可以包含多個
kbase
,分別對應
drl
的規則檔案
kbase name=“rules”
,name名稱需要保證唯一
kbase下面可以有一個或多個ksession,ksession的name屬性必須設定,且必須唯一
kbase的default屬性,表示當前KieBase是不是預設的,如果是預設的則不用名稱就可以查詢到該KieBase,但每個module最多隻能有一個預設KieBase
rules/tree。drl & 規則檔案
package rules;import org。itstack。demo。model。Policyimport org。itstack。demo。Result;global org。itstack。demo。Result res;rule “紅A”when Policy(sex == “男”, age < 18, userSingle) then res。setResult(“A”,“紅色A”); endrule “紅B”when Policy(sex == “男”, age < 18, !userSingle) then res。setResult(“B”,“紅色B”); endrule “紅C”when Policy(sex == “男”, age >= 18, age <= 30, userMarry) then res。setResult(“C”,“紅色C”); endrule “紅D”when Policy(sex == “男”, age >= 18, age <= 30, !userMarry) then res。setResult(“D”,“紅色D”); endrule “紅E”when Policy(sex == “男”, age > 30, userParenting) then res。setResult(“E”,“紅色E”); endrule “紅F”when Policy(sex == “男”, age > 30, !userParenting) then res。setResult(“F”,“紅色F”); endrule “黃A”when Policy(sex == “女”, age < 18, userSingle) then res。setResult(“A”,“黃色A”); endrule “黃B”when Policy(sex == “女”, age < 18, !userSingle) then res。setResult(“B”,“黃色B”); endrule “黃C”when Policy(sex == “女”, age >= 18, age <= 30, userMarry) then res。setResult(“C”,“黃色C”); endrule “黃D”when Policy(sex == “女”, age >= 18, age <= 30, !userMarry) then res。setResult(“D”,“黃色D”); endrule “黃E”when Policy(sex == “女”, age > 30, userParenting) then res。setResult(“E”,“黃色E”); endrule “黃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的啟動過程
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() 初始化
在初始化方法中,構建
KieServices。Factory。get();
,這個過程是比較耗費資源,實際業務使用中也不會頻繁的構建。
從
KieServices
中獲取
KieContainer
,用於給定KieModule的所有kiebase的容器。
設定FACT物件,其實就是你的決策物件的一些條件值。
test_drools() 執行規則
獲取kmodule。xml中配置中名稱為all-rules的session,預設為有狀態的。
設定決策物件
kieSession。insert(policy);
設定全域性物件
kieSession。setGlobal(“res”, result);
,用於最終把結果輸出
開始執行規則
kieSession。fireAllRules()
最終輸出結果,到最後釋放資源
kieSession。dispose()
測試結果
決策請求:{“age”:16,“sex”:“男”,“userMarry”:false,“userParenting”:false,“userSingle”:false}Fire rule(s):1決策結果(Drools):B|紅色B
在測試過程中可以嘗試修改入參資訊,以此驗證不同的結果。
五、Rete 演算法瞭解
Drools 是用 Java 語言編寫的開放原始碼規則引擎,使用 Rete 演算法對所編寫的規則求值。Drools 允許使用宣告方式表達業務邏輯。可以使用非 XML 的本地語言編寫規則,從而便於學習和理解。並且,還可以將 Java 程式碼直接嵌入到規則檔案中,這令 Drools 的學習更加吸引人。
好!那麼這樣你就知道,Drools的核心內容是關於 Rete 演算法的實現。接下來我們再來了解下 Rete。
為了解決生產式推理引擎效率底下的問題,Forgy 在1979年提出 Rete 演算法,作為生產式系統的高效模式匹配演算法。Rete 演算法的初衷是:利用規則之間各個域的公用部分減少規則儲存,同時儲存匹配過程的臨時結果以加快匹配速度。為了達到這種效果,演算法將規則拆分,其中每個條件單元作為基本單位(節點)連線成一個數據辨別網路,然後將事實經過網路篩選並傳播,最終所有條件都有事實匹配的規則被啟用。
Rete 演算法自從 1979 年提出以來,已經經歷過各種改進與推廣。除了對自身規則網路結構的最佳化外,對一些功能擴充套件如模糊推理、事件推理、並行化等也有很多研究。
1。 結構最佳化
混合邏輯符的處理邏輯運算子(operators)是指注入and、or、not等,的邏輯運算子處理。
規則前件的重排序規則前件順序是指規則體哦啊見中的各個約束的排列順序,它決定了條件連結操作的執行順序,影響中間結果的大小,是決定規則匹配效率的關鍵因素。
索引方法索引方法是指對 Rete 網路的節點建立當前節點對後繼 的索引,在事實斷言時可以透過索引快速找到對應的後繼節 點而無需逐個查詢。
2。 功能擴充套件
處理其他邏輯 Rete 最初只是用於處理一階布林邏輯,目前有很多 Rete 的擴充套件被用來處理其他邏輯。
帶時間資訊的事件處理 Rete 透過事實來表達當前狀態,但是很多應用包括一些事件流中的時間,在事件並行執行中起到關鍵作用。所以需要 Rete 演算法對這些資訊進行處理。
3。 特殊資料的推理
瑕疵資料與不確定性推理
不正確性
不精準性
不一致性
快速變化資料與機器學習除了資料瑕疵,對於變化劇烈的資料也成為Rete演算法需要解決的問題。
4。 並行化
Rete 演算法從提出至今,效能提升問題一直是研究重點。多核多處理器問世後,將推理過程分配到不同機器上並行處理成為一種常見的效率提升方法
六、總結
優秀的產品、優秀的研發,從來不只是傳話筒也不是工具機器。而是有靈魂的工匠,需要有謀有段,決策、遠見。
Drools的使用還不止是這一點,他還豐富的很,我們本章節主要是一個開篇,後續會繼續完善。
只有你的技術識棧足夠的全面,才能讓你在遇到一個問題的時候,有N中的方案。但學習一定是自己的事,無論是忙與閒,都要讓自己充充電。娛樂不是不可以,只不過要適當的控制下自己。
如果你控制不住自己,就會有別人控制你