內容提要
在本文的第一部分,我將討論規則引擎如何幫助你從軟件的應用邏輯中分離出商業規則邏輯,以實現商業應用的靈活性。在第二部分,我還將介紹
JSR
-
94
規則引擎
API
,及其實現原理。在第三部分
,
介紹其開源實現
Drools
項目,它是這一新技術的先驅,并詳解一個規則引擎例子。
?
一、規則引擎如何幫助你從軟件的應用邏輯中分離出商業規則邏輯,以實現商業應用的靈活性。
定義
:
規則引擎:由基于規則的專家系統中的推理引擎發展而來
,
是一種嵌入在應用程序中的組件,實現了將業務決策從應用程序代碼中分離出來,并使用預定義的語義模塊編寫業務決策。接受數據輸入,解釋業務規則,并根據規則做出業務決策。
為什么使用規則引擎
“任何事物都會改變,唯一不變的是變化”…,這些陳詞濫調已經在我們耳邊磨出糨子來了. 無論是快速軟件開發,極限編程,還是敏捷軟件開發等,它們 無一例外地強調靈活和變化的重要性。
雖然IT團隊反應迅速,但他們通常帶來"電話效應"――IT給商業計劃的執行帶來的阻力和它帶來的利益一樣多。不幸的是,在開發團隊完全理解商業決策規則并實現之前,規則已經改變了。在軟件進入市場前,它已經過時了,需要進行重構以滿足新的業務需求。如果你是一個開發人員,你會知道我在說什么。再也沒有比 在需求變動的情況下構造軟件讓開發人員更沮喪的事情了。作為軟件開發人員,你必須比業務人員更了解業務,有時還要了解更多。
??? 試想一下在完成產品開發時,需求(市場環境或人為因素,有些需求無法在項目初始就能預見的)和規劃產品開發時相比,已經發生了根本變化。現在你必須要遵守新的規則,你已經喪失了你的邊際優勢,而且設 計軟件的五人中的三人已經離開了公司。你必須給接手的新人重新講解復雜的業務。如果事情不順利,你可能發現自己要對付一個缺少文檔,并且你完全不了解的遺留應用。
你的戰略在哪出現了問題?你在哪里應該可以做到更好?當前在Java社區,一個引人注目的新技術是,分離商業決策者的商業決策邏輯和應用開發者的技術決策,并把這些商業決策放在中心數據庫,讓它們能在運行時(即商務時間)可以動態地管理和修改。這是一個你值得考慮的策略。
為什么你的開發團隊不得在代碼中包含復雜微妙的商業決策邏輯呢?你怎樣才能向他們解釋決策推理的微妙之處呢?你這樣做是否謹慎呢?可能不是。你一方面要應付市場,一方面要應付軟件代碼,這實在太困難了。如果能將這些商業決策規則集中地放在一個地方,以一種你可以理解的格式定義,讓你可以直接管理,而不是散落在代碼的各個角落,那該有多好。如果你能把商業決策規則獨立于你的軟件代碼,讓開發團隊作出技術決策,你將會獲得更多好處。你的項目開發周期會更短,軟件對于變動的需求更靈活。
?
分離業務和技術的關注點
這是一個非常簡單的例子,從業務人員的角度,說明如何分離商務和技術的關注點。
CRM
系統新裝
ADSL
。系統的一部分用于分析并受理可供客戶購買的銷售品
,
并在需要時向你提出相應的提示。這個系統的工作是,識別出產品,并標記出來以便進一步的檢查。
前置條件
:
CRM
開發團隊擁有一大堆數據,并開發了一系列你可以在規則中引用的簡單數據對象。現在,為簡單起見,假設你是一名受過良好教育的,了解技術的管理人,你了解
XML
的基本知識,可以讓你編寫和修改簡單的
XML
規則文件。
你的第一個規則是,給在眾多受理的產品
(
有上百個產品
,
且只有一個入口和一個出品
)
中剔除非
ADSL
的產品(這有點過分簡化,但這里只作為一個例子)
,
并給
ADSL
承載電話進行合法性校驗。該電話先前不能有承載的
ADSL.
。對于這個簡單的例子,你的規則文件看起來如下(我們將會過頭來討論這個文件的結構):
<
rule?
name
="Prd_1-0-A1-1307?-01?"
>
?
<
java:condition
>
map.get(?“?prdId?”?).equals("1307")
</
java:condition
>
?
<
java:condition
>
map.get(?“?count?”?)
<
1
</java:condition
>
?
<
java:consequence
>
bean.setPass(true);
</
java:consequence
>
?
</
rule
>
?
半個月后,你接到電信公司的電話,對于
ADSL
選擇寬帶上網卡的用戶承電話承載的
ADSL
可不受限制。除此之外,你的客戶還要求
XXX
本地網內的某類客戶承載電話承載的
ADSL
不能超過
5
個。
你啟動規則編輯器,并修改規則以匹配新的評估條件。完成規則文件修改后,看起來如下:
<
rule?
name
="Prd_1-0-A1-1307-01"
>
?

<
java:condition
>
map.get(?“?prdId?”?).equals("1307")
</
java:condition
>
?
<
java:condition
>
!map.get(?“?計費方式?”?).equals(?“?寬帶上網卡?”?)
</
java:condition
>
?
<
java:condition
>
map.get(?“?count?”?)
<
1
</java:condition
>
?
<
java:consequence
>
bean.setPass(true);
</
java:consequence
>
?
</
rule
>
???

<
rule?
name
="Prd_1-0-A1-1307-02"
>
?
<
java:condition
>
map.get(?“?prdId?”?).equals("1307")
</
java:condition
>
?
<
java:condition
>
map.get(?“?計費方式?”?).equals(?“?寬帶上網卡?”?)
</
java:condition
>
?
<
java:condition
>
(map.get(?“?latnId?”?).equals(?“?552?”?)&
&map
.get(?“?count?”?)<5)?||?!map.get(?“?latnId?”?).equals(?“?552?”?)
</
java:condition
>
?
<
java:consequence
>
bean.setPass(true);
</
java:consequence
>
?
</
rule
>
?
你無需為此向開發團隊作任何解釋。你無需等待他們開發或測試程序。如果你的規則引擎的語義足夠強大,讓你描述工作數據,你可以隨時按需修改商業規則。
現在,我希望你已經清楚以下的原則:在這個例子中,
ADSL
承載電話是否合法是一個業務決策,而不是技術決策。決定將哪個承載電話是否通過是業務人員的邏輯
。業務人員作出這些決策,并可以按需定制應用。這些規則因此變成了一種控制界面,一種新的商業系統用戶界面。
?
二、
Java
規則引擎
API JSR-94,
及其實現原理。
2003
年
11
月,
為了使規則引擎技術標準化,
Java
社區制定了
Java
規則引擎
API
(
JSR94
)規范。它為
Java
平臺訪問規則引擎定義了一些簡單的
API
。
這個新的
API
讓開發人員在運行時訪問和執行規則有了統一的標準方式。隨著新規范產品實現的成熟和推向市場,開發
團隊將可以從應用代碼中抽取出商業決策邏輯。
Java
規則引擎
API
在
javax.rules
包中定義,是訪問規則引擎的標準企業級
API
。
Java
規則引擎
API
允許客戶程序使用統一的方式和不
同廠商的規則引擎產品交互,就如同使用
JDBC
編寫獨立于廠商訪問不同的數據庫產品一樣。
Java
規則引擎
API
包括創建和管理規則集合的機制,在工作區
中添加,刪除和修改對象的機制,以及初始化,重置和執行規則引擎的機制。
Java
規則引擎
API
主要由兩大類
API
組成
:
規則管理
API(The Rules Administrator API)
和運行時客戶
API(The Runtime Client API)
。
?
Java
規則引擎是一種嵌入在
Java
程序中的組件,它的任務是把當前提交給引擎的
Java
數據對象與加載在引擎中的業務規則進行測試和比對,激活那些符合當前數據狀態下的業務規則,根據業務規則中聲明的執行邏輯,觸發應用程序中對應的操作。
?
Java
規則引擎的工作機制與上述規則引擎機制十分類似,只不過對上述概念進行了重新包裝組合。Java規則引擎對提交給引擎的Java數據對象進行檢 索,根據這些對象的當前屬性值和它們之間的關系,從加載到引擎的規則集中發現符合條件的規則,創建這些規則的執行實例。這些實例將在引擎接到執行指令時、依照某種優先序依次執行。一般來講,Java規則引擎內部由下面幾個部分構成:工作內存(Working Memory)即工作區,用于存放被引擎引用的數據對象集合;規則執行隊列,用于存放被激活的規則執行實例;靜態規則區,用于存放所有被加載的業務規則, 這些規則將按照某種數據結構組織,當工作區中的數據發生改變后,引擎需要迅速根據工作區中的對象現狀,調整規則執行隊列中的規則執行實例。Java規則引 擎的結構示意圖如下圖所示。

?
當引擎執行時,會根據規則執行隊列中的優先順序逐條執行規則執行實例,由于規則的執行部分可能會改變工作區的數據對象,從而會使隊列中的某些規則執行實例因為條件改變而失效,必須從隊列中撤銷,也可能會激活原來不滿足條件的規則,生成新的規則執行實例進入隊列。于是就產生了一種“動態”的規則執行鏈,形 成規則的推理機制。這種規則的“鏈式”反應完全是由工作區中的數據驅動的。
任何一個規則引擎都需要很好地解決規則的推理機制和規 則條件匹配的效率問題。規則條件匹配的效率決定了引擎的性能,引擎需要迅速測試工作區中的數據對象,從加載的規則集中發現符合條件的規則,生成規則執行實例。1982年美國卡耐基?梅隆大學的Charles L. Forgy發明了一種叫Rete算法,很好地解決了這方面的問題。目前世界頂尖的商用業務規則引擎產品基本上都使用Rete算法。
?
三、開源實現
Drools
項目
現在,我要介紹
Drools
項目,
Charles Forgy Rete
算法的一個增強的
Java
語言實現。
Drools
是一個
Bob McWhirter
開發的開源項目,放在
The Codehaus
上。在我寫這篇文章時,
Drools
發表了
2.0-beata-14
版。在
CVS
中,已完整地實現了
JSR94 Rule Engine API
并提供了單元測試代碼。
Rete
算法是
Charles Forgy
在
1979
年發明的,是目前用于生產系統的效率最高的算法(除了私有的
Rete II
)。
Rete
是唯一的,效率與執行規則數目無關的決策支持算法。
For the uninitiated, that means it can scale to incorporate and execute hundreds of thousands of rules in a manner which is an order of magnitude more efficient then the next best algorithm
。
Rete
應用于生產系統已經有很多年了,但在
Java
開源軟件中并沒有得到廣泛應用(討論
Rete
算法的文檔參見
http: //herzberg.ca.sandia.gov/jess/docs/61/rete.html
。)。
除了應用了
Rete
核心算法,開源軟件
License
和
100
%的
Java
實現之外,
Drools
還提供了很多有用的特性。其中包括實現了
JSR94 API
和創新的規則語義系統,這個語義系統可用來編寫描述規則的語言。目前,
Drools
提供了三種語義模塊
――Python
模塊,
Java
模塊和
Groovy
模塊。本文余下部分集中討論
JSR94 API
,我將在第二篇文章中討論語義系統。
作為使用
javax.rules API
的開發人員,你的目標是構造一個
RuleExecutionSet
對象,并在運行時通過它獲得一個
RuleSession
對象。為了簡化這個過程,
我編寫了一個規則引擎
API
的
fa?ade
,可以用來解釋代表
Drools
的
DRL
文件的
InputStream
,并構造一個
RuleExecutionSet
對象。
在上面提到了
Drools
的三種語義模塊,我接下來使用它們重新編寫上面的例子
XML
規則文件。這個例子中我選擇
Java
模塊。使用
Java
模塊重新編寫的規則文件如下:
<
rule-set?
name
="fibonacci"
?xmlns
=http://drools.org/rules
???????xmlns:java
="http://drools.org/semantics/java"
?xmlns:xs
=http://www.w3.org/2001/XMLSchema-instance
???xs:schemaLocation
="http://drools.org/rules?rules.xsd?http://drools.org/semantics/java?java.xsd"
>
<
import
>
java.util.*
</
import
>
<
application-data?
identifier
="request"
>
javax.servlet.http.HttpServletRequest
</
application-data
>
<
java:functions
>
public?static?int?countAdslByTel(HttpServletRequest?request)throws?Exception{
???????PrdInstDAO?prdInstDAO=new?PrdInstDAO?(request.getParameter(“latnId”));
???????Return?prdInstDAO.countAdslByTel(request.getParameter(“service_nbr”))?;
}

</
java:functions
>
<
rule?
name
="Prd_1-0-A1-1307"
?salience
="1"
?no-loop
="true"
>
<
parameter?
identifier
="map"
><
class
>
Map
</
class
></
parameter
>
<
parameter?
identifier
="bean"
>
<
class
>
AcceptReqBean
</
class
></
parameter
>
<
java:condition
>
map.get(“prdId”).equals("1307")
</
java:condition
>
<
java:consequence
>
Int?count=??????countAdslByTel(request);
If(map.get(“計費方式”).equals(“寬帶上網卡”)){
???????If(map.get(“latnId”).equals(“552”)&
&count
>=5){
???????bean.setPass(false);
}else
???bean.setPass(true);
}else{
???????If(count
<
1
){
bean.setPass(true);
}else{
???????bean.setPass(false);
???????}
}
</java:consequence
>
</
rule
>
</
rule-set
>
現在的規則文件并沒有上面的簡潔明了。別擔心,我們將在下一篇文章討論語義模塊。現在,請注意觀察XML文件的結構。其中一個rule-set元素包含了一個或多個rule元素,rule元素又包含了parameter,condition和consequence元素。Condition和 consequence元素包含的內容和Java很象。注意,在這些元素中,有些事你可以做,有些事你不能做。目前,Drools使用 BeanShell2.0b1作為它的Java解釋器。我在這里并不想詳細的討論DRL文件和Java語義模塊的語法。我們的目標是解釋如何使用 Drools的JSR94 API。
在Drools項目CVS的drools-jsr94模塊中,單元測試代碼包含了一個ExampleRuleEngineFacade對象,它基于 Brian Topping的Dentaku項目。這個facade對象通過javax.rules API,創建了供RuleExecutionSet和RuleSession使用的一系列對象。它并沒有完全包括了Drools引擎API的所有特性和細微差別,但可以作為新手使用API的一個簡單例子。
下面的代碼片斷顯示如何在程序中調用drools規則引擎并執行。
?
//
?執行規則引擎
????????RulesEngine?rulesEngine
=
new
?RulesEngine(url);

????????List?rulesResult
=
rulesEngine.executeRules(
new
?WorkingEnvironmentCallback()
{

????????????
public
?
void
?initEnvironment(WorkingMemory?workingMemory)?
throws
?FactException
{
??????????????????????workingMemory.assertObject(..);
??????????????????????workingMemory.setApplicationData(
"
map
"
,map);
..
????????????}
????????}
);
結束語
規則引擎技術為管理多變的業務邏輯提供了一種解決方案。規則引擎既可以管理應用層的業務邏輯又可以使表示層的頁面流程可訂制。這就給軟件架構師設計大型信息系統提供了一項新的選擇。而
Java
規則引擎在
Java
社區制定標準規范以后必將獲得更大發展。
posted on 2006-11-27 22:15
jacky 閱讀(2997)
評論(0) 編輯 收藏