Drools——爲什麼使用規則

目錄

爲什麼使用規則

規則獨立

規則執行鏈

規則的原子性

規則順序

規則執行生命週期

規則之間的協作

使用BRMS讓更多人蔘與到規則開發

讓規則引擎做這些事情

規則引擎算法


爲什麼使用規則

關於爲什麼規則很有用,在這一點上,你大概一直都有點困惑。如果我們這樣想,就一條或幾條規則而言,我們認爲可能直接使用類似於Java的命令式代碼會更好一些。作爲開發者而言,我們通常將需求拆分成我們將要遵循的步驟列表,並且避免所有事情被集中掌控。業務規則背後主要的優勢無法在一個或幾個規則中體現,而是體現在由大量的、隨時變化的規則組定義的一個非常複雜的系統,如果要使用常規代碼實現它將會需要很大的工作量去維護它。

隨着業務規則代碼的增長,很多規則共同工作來定義一個複雜的系統。無論我們需要實現新得需求、更新現有的需求、替換參數、或者使用意想不到的方法改變系統行爲的結構,我們唯一要對規則做的事情是實現現在需要的規則,移除現在不需要的規則。業務規則大概就是基於以下原則:

  • 相互獨立
  • 易於更新
  • 每條規則控制所需要儘量小的範圍(單一性)
  • 允許不同背景的人相互協作

規則獨立

一個業務規則不需要做太多事情。一個大的基於規則的業務系統就是它是由大量的規則相互交互組成。然而,大多數時間,規則都不需要直接知道這種交互。這就是我們說的規則之間的相互獨立的意思。每條規則應該能監測到特定的一組情況並且能做出行動,而不需要其領域之外的其他任何數據。

當我們思考規則時,我們通常有很多方式可以形象化的描述它。拿起一本法律書籍,你會發它們是由一組規則組成的,每一組都是以條款形式存在。大多數都是提出當前的場景以及在這個場景下相應的行爲和解釋。這些大多數條款都不會提及到其他的條款。這麼做的原因是讓規則更易於閱讀和理解和更不容易被誤解。

當你爲組織定義規則時同樣適用於這些原則。每條規則應該嘗試不依賴其他規則。反而,規則應該只依賴於領域所提供的數據。這會讓規則具備自解釋特性,而不必再除了業務規則的內容之外,附加其他的解釋。

然後有時候規則確實會以間接的方式依賴其他規則。假設我們制定的規則會在另一個規則的條件下被執行。這種假設下創建的數據我們稱之爲推論,它們對於擴展規則的可用性非常有用。

規則執行鏈

正如上個章節提到的那樣,一個好的業務規則是一個相互獨立的實體,除了領域數據不依賴任意其他東西來決定其意義。這並不是說每個規則都工作在一個非常複雜的結構體上,否則你會得到一個難以維護的非常複雜的規則。

如果一個規則很複雜,你需要將其拆分爲多個更小的規則。然而,儘管這麼說,規則的獨立性還是非常重要的,你不應該在規則中明確的調用其他規則。這意味着使用順序流來進行控制,而我們已經表述過,聲明式編程不允許這種情況。

相反,我們可以通過對基礎領域和向基礎領域添加的信息做出假設 來進行分割複雜度。這些假設稱之爲推理。作爲條件的一部分,其他規則就會用到這個新信息,不管其如果決定。稍後我們看下面的例子來理解如何完成對規則的拆分:

  • 當我們聽到火警鈴響的時候,我們就推測發生了火情
  • 當發生了火情,我們需要打給消防隊
  • 當消防隊到了之後,我們讓消防隊員救火

這三個規則可以組合成一個複雜的規則:當我們聽到火警鈴聲響的時候,我們打電話給消防隊,並讓他們救火。然而通過將其拆分爲了三個簡單的組件,我們可以更簡單的擴展規則引擎的可用性。我們可以重用我們制定的第一個推理:現在有火情。然後去觸發其他的行爲,比如說啓動應急噴頭、關閉電源或者打電話給保險公司。

當一個規則失去意義的時候,我們可以從規則引擎中移除它。當我們需要一個新規則的時候,我們可以創建它,並充分利用現有有用的推理數據。由於順序流由規則引擎進行控制,我們不需要擔心規則代碼執行的順序或者新創建的規則 適合 放在當前其餘現有的規則之間的哪裏。

規則的原子性

利用現有的推理引擎,隨着我們創建更多的規則,規則越簡單,它們就越容易擴展。因此讓規則儘可能簡單是創建一個好的業務規則的原則之一 ,簡單到不能再劃分成更小的規則,這也是一個規則應該考慮的。這個原則我們稱之爲原子性。

具備原子性的規則是簡單易懂的,它們通常設計爲由少量的條件來決定行爲,或者是推斷某種情況發生。因爲它們是相互獨立的,所以它們具備自描述性。規則原子性、獨立性和推斷能力一起使用業務規則可以成爲我們定義系統的任何行爲的最簡單的組件。

簡潔性允許我們的系統可以明確的知道爲什麼要做這個決策,讓規則具備自描述性並允許我們可以追蹤在干預決策時的每個規則。這也是爲什麼幾千年來法律一直是社會內部規則的基石的原因。

規則順序

我已經提到過,規則不會遵循特定的順序。順序流取決於規則引擎。規則引擎會基於領域中的有用的數據進行決策規則以什麼樣的順序執行。這也意味着規則定義的順序是不重要的,只需要與規則的條件能匹配的數據。

當相同的條件出現在領域中,有一些方法對多個競爭執行的規則進行排序。此排序作爲規則的第二優先級,而領域模型中的數據作爲決定規則被激活的首要條件。這些排序機制我們會在後面的章節結合一些特殊的情況進行討論。我們也會定義跟通常情況不一樣的規則。當我們編寫規則的時候,如果你發現我們控制着所有單個規則的執行順序,應該重新考慮下正在編寫的規則的定義方式。

在聲明式編程時,開發人員很難僅僅是看一眼就明白了。儘管如此,在基於順序無關緊要的基礎上,它提供了很多增加開發和運行時的效率的改進,讓我們可以在任何地方添加規則。這樣做的好處:

  • 規則之間的協作更易於管理
  • 衝突更容易避免
  • 更多的人可以參與到規則的開發,讓規則開發可以包容各方面的人成爲現實

規則執行生命週期

規則引擎優化的了條件的評估,並確保我們儘可能快的方式決定要觸發的規則。然後規則引擎無法立刻執行我們的規則,除非我們指定這麼做。當我們找到規則評估對於一組數據爲真的時候,規則和觸發的數據會被添加到一個集合中。規則的生命週期由兩部分組成,並被明確劃分爲規則評估和規則執行部分。規則評估會把規則行爲和觸發它們的數據添加到一個稱之爲Agenda的組建組建中。當規則執行命令發出後,我們會喚醒規則引擎觸發所在在Agenda中的規則。

正如先前說的那樣,我們不會控制規則什麼時候被被觸發。至於什麼時候觸發,這是規則引擎的責任,規則引擎由我們創建的業務規則和提供給引擎的數據。然後一旦規則引擎決定規則要被觸發,我們便可以掌控規則觸發的時間。這是通過調用規則引擎的方法來完成的。

一旦規則被觸發,Agenda中所有匹配的規則都會被觸發。規則引擎會更新領域中的數據,如果這些更新產生的新數據會匹配新的規則,新匹配的規則會添加到Agenda中,或者如果這些更新導致一些匹配的規則不再爲真,它們將會被取消。這個完整的週期會一直持續到 對於規則引擎的有用的數據來說,Agenda中沒有可用的規則或規則引擎的引擎被迫停止。下面的圖展示了這個工作流是如何執行的:

這個執行生命週期會一直執行完所有基於我們定義的規則和提供的領域數據規則引擎決定添加到Agenda中的規則。有些規則可能不會執行,有些規則可能會被執行多次。在接下來的章節中,我們將會學習如何控制規則的執行。我們要一直保持已經建立了的業務規則的編寫原則:獨立性和原子性。我們學到更多關於規則引擎的配置,就越相信它可以做這件事情。那一刻,將會是信心的飛躍,但是,每一步我們都會學到如何去控制規則引擎,直到可以100%的確認它可以精確的做到我們期待的那樣。

規則之間的協作

隨着規則的創建,順序流逐漸超出我們的直接控制,這時最大的優勢就是我們不必擔心代碼寫在哪裏。所有的規則都是獨立的,規則執行的順序流由規則引擎在運行時決定,不論規則代碼到底寫在哪裏。

使用常見的命令式編程語言,比如Java,在程序執行階段每個指令都會在特定的時間發生,我們需要在代碼中找到指定的點,然後添加我們的更新,還涉及到review整套代碼。整個設計模式都在圍繞着管理這個侷限性,提供可以讓工作在同一系統的開發者可以相互協作的途徑。每個主要的設計模式都可以將代碼組分割爲modules、methods、classes, 以便可以輕鬆的管理開發者之間的協作。

然而命令式編程最主要的限制是一旦系統設計完畢,我們不能輕鬆突破我之前分割的代碼的限制。當我們創建設計的時候,必須要預測未來會發生的變化——這些變化會很難被實現。如果我們不能做到這樣,多個開發者可能會因爲不同的需求修改相同的代碼片段,這些代碼很容易發生衝突。

這種限制在響應式編程中可以避免,因爲它並不關心規則的順序。在相同的領域模塊中,不同的人可以在沒有衝突的情況下定義不同切面進行協作,因爲新添加的規則放到已存在的規則的哪個位置都可以。不論執行的順序,執行的結果都相對一樣。

讓我們看一下下面命令式編程和響應式編程對比的僞代碼。當我們向命令式代碼中添加更新時,我們不能在任何位置做這件事情。如果你把它放在不同的地方,特定的地方會得到特定的更新語義,它既不能像期待的那樣工作或者不能執行,你會在下面看到對比:

另一方面,每個業務規則都被定義爲一個孤立的代碼塊。規則編寫人員可以在沒有任何問題的情況下,在任何地方做添加工作。這使得在協作環境下的應用開發更容易,因爲它更不容易出現衝突問題,如下圖所示:

我們更少的機會衝突,那麼我們就可以集中時間和精力在如何成功構建系統上,而不是擔心不同的組件和組件內部進行合併的解決方案。在沒有衝突的情況下,有更多可能的點去添加代碼,這也讓更多的人蔘與到開發週期中成爲可能。這將會大幅度加快開發和軟件更新速度。

使用BRMS讓更多人蔘與到規則開發

由於業務規則在程序開發期間提供的更高的可協作性,我們可以在系統中增加很多人去定義規則決策。當前緊跟着我要面對的瓶頸是找到更多知道如何編寫規則的的人。編寫規則是一件技術活,至少在剛開始是這樣。這需要一定水平的知識在如何去定義條件和行爲上——我們將在下一個章節進行覆蓋的主題,可以讓更多的人僅用一點時間就可以學會如何編寫規則。

即使我們能讓技術人員快速的學會如何編寫規則,那也是不夠的。這並不是由於技術限制的原因,而是由於掌握了可寫成業務規則的實用知識的人並不一定技術嫺熟或最可用。當然,也可能是這樣,你大概已經找到一組最好的可以工作在基於業務規則的系統中的成員。然而,在大多數情況下,他們有實用的知識,但沒有時間去設計和學習如何寫技術規則。

對於這些組中的業務專家來說,這是一個可以讓他們以友好的方式來編寫規則的平臺。這些由一些比較友好的編輯器的組合而成,並且具有版本控制和發佈功能的平臺,我們稱之爲業務規則管理系統(BRMS).基本上,業務規則專家會使用它們熟悉的日常語言去創建規則,並用其進行定義決策。你將會在第五章學習到更多關於友好的編寫規則的方法。現在提一下,我們可以使用編輯器用自然語言進行定義規則,這允許業務專家可以像技術專家一樣的速度來定義業務規則。下面的截圖我們可以看到drools的業務規則管理系統KIE Workbench裏的一個編輯器。

讓規則引擎做這些事情

至今爲止,我們涵蓋介紹了業務規則的結構。每當解釋規則是如何執行的時候,我們總是說規則引擎會做好它。當我們使用規則引擎的時候,我們相信規則引擎會基於我們提供的領域數據來決定規則的觸發。當前階段,我們會嘗試去定義規則引擎是如何定義規則何時執行。前面的章節中,我們簡單的展示了業務規則是如何轉換爲決策樹的,它遵循響應式編程的規範,基於數據進行決策。在這個章節,我們會嘗試去解釋這個結構是如何基於我們定義的規則幫助我們生成更高效的決策樹。

規則引擎算法

規則引擎使用特定的算法將我們定義的規則轉化爲可執行的決策樹。決策樹的性能取決於生成算法的優化方式。drools6框架專注於更高的性能而定義了屬於自己的算法。這個算法的名字是PHREAK,它的創始人是Mark Proctor。它是對現有由Charles Forgy發明的算法RETE的一系列的優化和重新設計。PHEARK是到現在爲止開源代碼中最高效和可行的算法。

在生成的決策樹中,在我們規則中的每個條件都會轉化爲樹中的一個節點,不同的條件如何與其他條件聯繫的取決於這些節點的連接。隨着我們往規則引擎添加數據,規則將會被批量評估,使用最優路徑流經網絡。當數據到達葉子節點的時候,決策樹執行完畢,葉子節點表示要響應的規則。這些規則會添加到一個集合中,當執行一個命令就會觸發集合中所有的規則或規則組。

由於對規則中條件的連續實時評估,規則引擎爲了效率將在內存中爲所有數據提供規則的評估。我們將在後面的章節講述算法是如何創建決策樹的。

每當我們添加很多數據到規則引擎中的時候,它們會從根節點進入決策樹。決策樹的每個優化都是根據以下幾點來工作的:

  • 嘗試將所有的條件打破爲更小數量的單元,爲了可以在決策樹中儘可能的重用。
  • 嘗試在去到下層節點的時候僅需要一個操作,直到條件的評估得到false或到達葉子節點,並沒標記爲可執行的規則

每一片數據在評估時都使用儘可能高效的方式。對這些評估的優化是規則引擎的主要關注點。在接下來的章節,我們將會討論爲了儘量快的創建我們自己的規則,如何充分利用這些優勢製作規則。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章