這種場景你還寫ifelse你跟孩子坐一桌去吧


做者:小傅哥
博客:https://bugstack.cn - 彙總系列專題文章html

沉澱、分享、成長,讓本身和他人都能有所收穫! 😄

1、前言


你見過這樣的代碼嘛?相似的呢?嗯,那麼恭喜你被這個世界溫柔以待!java

if else,並非一個很是壞的關鍵字,只不過有人把他用壞了。尤爲在接到產品需求以下這樣;git

日期 需求 緊急程度 程序員(話外音)
星期一.早上 猿哥哥,老闆說要搞一下營銷拉拉量,給男生女生髮不一樣的優惠券,促活消費。 很緊急,下班就要 行吧,也不難,加下判斷就上線
星期二.下午 小哥哥,我們上線後很是好。要讓我們按照年輕、中年、成年,不一樣年齡加下判斷,準確刺激消費。 超緊急,明天就要 也不難,加就加吧
星期三.晚上 喂,小哥哥!睡了嗎!老闆說我們此次活動很成功,能夠不能夠在細分下,把單身、結婚、有娃的都加上不一樣判斷。這樣更能刺激用戶消費。 賊緊急,最快上線。 已經意識到ifelse愈來愈多了
星期四.凌晨 哇!小哥哥大家太棒了,上的真快。嘻嘻!有個小請求,須要調整下年齡段,由於如今學生處對象的都比較早,有對象的更容易買某某某東西。要改下值!辛苦辛苦! 老闆,在等着呢! 一大片的值要修改,哎!這麼多ifelse
星期五.半夜 歪歪喂!巴巴,壞了,怎麼發的優惠券不對了,有客訴了,不少女生都來投訴。你快看看。老闆,他... (一頭汗),哎,值粘錯位置了! 終究仍是一我的扛下了全部

這樣的場景你是否有遇到過呢,那麼是產品給你代溝裏去了,仍是你把項目帶溝裏去了。可能會以爲,這東西這麼着急要,我也沒辦法呀。其實不止你沒有辦法,是爲了打下市場,讓每個人都很匆忙。只有合理的評估、鋪墊、架設,纔會不斷知足業務需求、產品形態的變化。不然日後的路愈來愈難!程序員

2、場景

對於上面所提到的這種場景,在咱們實際開發中是常常會遇到的。尤爲是在一些;營銷、風控、人羣等,各類用戶信息決策樹關係時,都會出現這樣的業務邏輯。並且對於一些較大場景是確定不會直接硬編碼if else,由於太難以維護。固然除非你這東西就寫一次用一次,下次不用了那無所謂。github

接下來咱們把上面的場景進行轉換一種樹結構圖,依次來體現出這個需求的全貌,以下;web

  • 從上圖咱們看到,把產品一週提的需求彙總後就一張樹形的決策流。每一種不一樣的因子均可以致使結果不一樣的走向。
  • 而若是這個產品總體的內容,從一點點交給你,和一整套交給你,你所作出來的研發設計是不一樣的。固然也有相同的,由於還有一部分頗有遠見的程序員,他們常年踩坑!而這份相同的高等的設計,就是踩坑踩出來的經驗。
  • 那麼,除了if else你還能在本身掌握的技術棧中想到什麼解決方案嗎?接下來,咱們會寫出兩種實現方式,用做比對。

3、if、else編碼

@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寫出來仍是沒問題的,只不過寫錯不錯就不必定了,畢竟一層套一層。這還算少的!

4、規則引擎Drools

關於規則引擎簡單說呢就是,將你業務邏輯中那些行爲規則流程變化的部分,分離出來。交給單獨的規則引擎進行處理。最終你只須要按照約定提供配置和入參,就能夠達到規則的執行結果。算法

Drools(JBoss Rules )具備一個易於訪問企業策略、易於調整以及易於管理的開源業務規則引擎,符合業內標準,速度快、效率高。業務分析師或審覈人員能夠利用它輕鬆查看業務規則,從而檢驗是否已編碼的規則執行了所需的業務規則。

上去就是一巴掌,而後在問爲何。好,先來把上面的代碼用Drools處理下,以後再解釋。網絡

1. 環境配置

  1. jdk1.8.0
  2. idea + maven3.x
  3. drools 7.32.0.Final
  4. 案例源碼下載,關注公衆號:bugstack蟲洞棧 回覆:源碼獲取
  5. 可視化流程圖解決方案;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
  • 關於案例中出現的代碼,能夠經過關注公衆號獲取:bugstack蟲洞棧,回覆關鍵字<獲取源碼>
  • 以上是咱們關於使用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 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 & 規則文件
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的啓動過程
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() 初始化session

  1. 在初始化方法中,構建KieServices.Factory.get();,這個過程是比較耗費資源,實際業務使用中也不會頻繁的構建。
  2. KieServices中獲取KieContainer,用於給定KieModule的全部kiebase的容器。
  3. 設置FACT對象,其實就是你的決策對象的一些條件值。

test_drools() 執行規則app

  1. 獲取kmodule.xml中配置中名稱爲all-rules的session,默認爲有狀態的。
  2. 設置決策對象kieSession.insert(policy);
  3. 設置全局對象kieSession.setGlobal("res", result);,用於最終把結果輸出
  4. 開始執行規則kieSession.fireAllRules()
  5. 最終輸出結果,到最後釋放資源kieSession.dispose()

測試結果

決策請求:{"age":16,"sex":"男","userMarry":false,"userParenting":false,"userSingle":false}
Fire rule(s):1
決策結果(Drools):B|紅色B
  • 在測試過程當中能夠嘗試修改入參信息,以此驗證不一樣的結果。

5、Rete 算法瞭解

Drools 是用 Java 語言編寫的開放源碼規則引擎,使用 Rete 算法對所編寫的規則求值。Drools 容許使用聲明方式表達業務邏輯。可使用非 XML 的本地語言編寫規則,從而便於學習和理解。而且,還能夠將 Java 代碼直接嵌入到規則文件中,這令 Drools 的學習更加吸引人。

好!那麼這樣你就知道,Drools的核心內容是關於 Rete 算法的實現。接下來咱們再來了解下 Rete。

爲了解決生產式推理引擎效率底下的問題,Forgy 在1979年提出 Rete 算法,做爲生產式系統的高效模式匹配算法。Rete 算法的初衷是:利用規則之間各個域的公用部分減小規則存儲,同時保存匹配過程的臨時結果以加快匹配速度。爲了達到這種效果,算法將規則拆分,其中每一個條件單元做爲基本單位(節點)鏈接成一個數據辨別網絡,而後將事實通過網絡篩選並傳播,最終全部條件都有事實匹配的規則被激活。

Rete 算法自從 1979 年提出以來,已經經歷過各類改進與推廣。除了對自身規則網絡結構的優化外,對一些功能擴展如模糊推理、事件推理、並行化等也有不少研究。

1. 結構優化

  1. 混合邏輯符的處理

    邏輯操做符(operators)是指注入and、or、not等,的邏輯運算符處理。

  2. 規則前件的重排序

    規則前件順序是指規則體哦啊見中的各個約束的排列順序,它決定了條件連接操做的執行順序,影響中間結果的大小,是決定規則匹配效率的關鍵因素。

  3. 索引方法

    索引方法是指對 Rete 網絡的節點創建當前節點對後繼 的索引,在事實斷言時能夠經過索引快速找到對應的後繼節 點而無需逐個查找。

2. 功能擴展

  1. 處理其餘邏輯
    Rete 最初只是用於處理一階布爾邏輯,目前有不少 Rete 的擴展被用來處理其餘邏輯。
  2. 帶時間信息的事件處理
    Rete 經過事實來表達當前狀態,可是不少應用包括一些事件流中的時間,在事件並行執行中起到關鍵做用。因此須要 Rete 算法對這些信息進行處理。

3. 特殊數據的推理

  1. 瑕疵數據與不肯定性推理

    • 不正確性
    • 不精準性
    • 不一致性
  2. 快速變化數據與機器學習

    除了數據瑕疵,對於變化劇烈的數據也成爲Rete算法須要解決的問題。

4. 並行化

Rete 算法從提出至今,性能提高問題一直是研究重點。多核多處理器問世後,將推理過程分配到不一樣機器上並行處理成爲一種常見的效率提高方法

6、總結

  • 優秀的產品、優秀的研發,歷來不僅是傳話筒也不是工具機器。而是有靈魂的工匠,須要有謀有段,決策、遠見。
  • Drools的使用還不止是這一點,他還豐富的很,咱們本章節主要是一個開篇,後續會繼續完善。關於工程代碼能夠關注公衆號(bugstack蟲洞棧)進行獲取。
  • 只有你的技術識棧足夠的全面,才能讓你在遇到一個問題的時候,有N中的方案。但學習必定是本身的事,不管是忙與閒,都要讓本身充充電。娛樂不是不能夠,只不過要適當的控制下本身。若是你控制不住本身,就會有別人控制你

7、彩蛋

CodeGuide | 程序員編碼指南 Go!
https://github.com/fuzhengwei/CodeGuide/wiki
本代碼庫是做者小傅哥多年從事一線互聯網 Java 開發的學習歷程技術彙總,旨在爲你們提供一個清晰詳細的學習教程,側重點更傾向編寫Java核心內容。若是本倉庫能爲您提供幫助,請給予支持(關注、點贊、分享)!

CodeGuide | 程序員編碼指南

相關文章
相關標籤/搜索