一.什么是Easy Rules
Easy Rules 是一款 Java 規(guī)則引擎互例,它的誕生啟發(fā)自有Martin Fowler 一篇名為 "Should I use a Rules Engine?" 文章邮丰。
該文文章寫(xiě)道:您可以自己構(gòu)建一個(gè)簡(jiǎn)單的規(guī)則引擎膊存。您所需要的就是創(chuàng)建一組具有條件和操作的對(duì)象急膀,將它們存儲(chǔ)在集合中,并遍歷它們以評(píng)估條件并執(zhí)行操作馏段。
這正是Easy Rules所做的砍鸠,它提供了規(guī)則抽象來(lái)創(chuàng)建帶有條件和操作的規(guī)則,以及運(yùn)行一組規(guī)則來(lái)評(píng)估條件和執(zhí)行操作的RulesEngine API驶赏。
二.核心特性
- 輕量級(jí)庫(kù)和易于學(xué)習(xí)的API炸卑;
- 基于POJO的注釋編程模型開(kāi)發(fā);
- 用于定義業(yè)務(wù)規(guī)則并使用Java輕松應(yīng)用它們的有用抽象;
- 能夠從原始規(guī)則創(chuàng)建復(fù)合規(guī)則;
- 使用表達(dá)式語(yǔ)言定義規(guī)則的能力;
三.運(yùn)行環(huán)境
EasyRules是一個(gè)Java庫(kù)煤傍。它需要Java 1.7+運(yùn)行環(huán)境盖文。
四.maven坐標(biāo)
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-support</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-mvel</artifactId>
<version>3.2.0</version>
</dependency>
五.
1.名詞解釋
- Name:規(guī)則名稱(chēng)空間中的唯一規(guī)則名稱(chēng)
- Description:規(guī)則的簡(jiǎn)要說(shuō)明
- Priority:規(guī)則優(yōu)先級(jí)
- Facts:規(guī)則運(yùn)行時(shí)已知的一些數(shù)據(jù)
- Conditions:為了應(yīng)用規(guī)則,應(yīng)根據(jù)某些事實(shí)應(yīng)滿(mǎn)足的條件集
- Actions:滿(mǎn)足條件時(shí)要執(zhí)行的操作集(可以添加/刪除/修改事實(shí))
2.定義Facts
Facts API是一組規(guī)則需要檢驗(yàn)的facts的抽象蚯姆。在內(nèi)部五续,F(xiàn)acts持有HashMap<String,Object>龄恋,這意味著:
- Facts 命名必須唯一且不能為空;
- 任何Java對(duì)象都可以當(dāng)做Fact.
說(shuō)白了 Facts 就是一個(gè) HashMap<String,Object>,里面都是一些需要校驗(yàn)規(guī)則的參數(shù)字段.
下面是一個(gè)定義Facts例子:
Facts facts = new Facts();
facts.add("rain", true);
Facts可以用在condition 和action 方法中,在參數(shù)前面使用@Fact注解:
@Rule
class WeatherRule {
@Condition
public boolean itRains(@Fact("rain") boolean rain) {
return rain;
}
@Action
public void takeAnUmbrella(Facts facts) {
System.out.println("It rains, take an umbrella!");
// can add/remove/modify facts
}
}
3.規(guī)則引擎
3.1 從3.1版開(kāi)始疙驾,Easy Rules提供了規(guī)則引擎接口的兩種實(shí)現(xiàn):
- DefaultRulesEngine:根據(jù)它們的自然順序(默認(rèn)優(yōu)先級(jí))應(yīng)用規(guī)則;
- InferenceRulesEngine:對(duì)已知事實(shí)持續(xù)應(yīng)用規(guī)則,直到不再適用任何規(guī)則郭毕。
3.2 創(chuàng)建規(guī)則引擎
要?jiǎng)?chuàng)建規(guī)則引擎它碎,可以使用每個(gè)實(shí)現(xiàn)的構(gòu)造函數(shù):
RulesEngine rulesEngine = new DefaultRulesEngine();
// or
RulesEngine rulesEngine = new InferenceRulesEngine();
然后,您可以按照以下方式運(yùn)行注冊(cè)規(guī)則:
rulesEngine.fire(rules, facts);
3.3 規(guī)則引擎參數(shù)
- skipOnFirstAppliedRule參數(shù)告訴引擎在應(yīng)用規(guī)則時(shí)跳過(guò)下一個(gè)規(guī)則显押。
- skipOnFirstFailedRule參數(shù)告訴引擎在規(guī)則失敗時(shí)跳過(guò)下一個(gè)規(guī)則扳肛。
- skipOnFirstNonTriggeredRule參數(shù)告訴引擎跳過(guò)下一個(gè)規(guī)則,沒(méi)有觸發(fā)規(guī)則乘碑。
- rulePriorityThreshold參數(shù)告訴引擎在優(yōu)先級(jí)超過(guò)定義的閾值時(shí)跳過(guò)下一個(gè)規(guī)則挖息。
您可以使用RulesEngineParameters API指定這些參數(shù):
RulesEngineParameters parameters = new RulesEngineParameters()
.rulePriorityThreshold(10)
.skipOnFirstAppliedRule(true)
.skipOnFirstFailedRule(true)
.skipOnFirstNonTriggeredRule(true);
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
如果您想從引擎獲得參數(shù),可以使用以下代碼片段:
RulesEngineParameters parameters = myEngine.getParameters();
六.HelloWorld
1.使用注釋定義規(guī)則
Easy Rules提供了可以將POJO轉(zhuǎn)換為規(guī)則的@Rule注釋蝉仇。下面是一個(gè)例子:
@Rule(name = "Hello World rule", description = "Always say hello world")
public class HelloWorldRule {
@Condition
public boolean when() {
return true;
}
@Action
public void then() throws Exception {
System.out.println("hello world");
}
}
@Condition注解標(biāo)記了執(zhí)行來(lái)評(píng)估規(guī)則條件的方法旋讹。這個(gè)方法必須是公共的,可能有一個(gè)或多個(gè)參數(shù)注釋@Fact并返回一個(gè)布爾類(lèi)型轿衔。只有一個(gè)方法可以用@Condition注釋進(jìn)行注釋沉迹。
@Action注釋標(biāo)記用于執(zhí)行規(guī)則操作的方法。規(guī)則可以有多個(gè)操作害驹”夼唬可以使用order屬性以指定的順序執(zhí)行操作。默認(rèn)情況下宛官,動(dòng)作的順序是0葫松。
運(yùn)行策略
public class Launcher {
public static void main(String[] args) {
// create facts
Facts facts = new Facts();
// create rules
Rules rules = new Rules();
rules.register(new HelloWorldRule());
// create a rules engine and fire rules on known facts
RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, facts);
}
}
七.復(fù)合規(guī)則
EasyRules允許您從原始規(guī)則創(chuàng)建復(fù)雜規(guī)則瓦糕。CompositeRule是由一組規(guī)則組成的。
復(fù)合規(guī)則是一個(gè)抽象概念腋么,因?yàn)榻M合規(guī)則可以以不同的方式觸發(fā)咕娄。在3.2版中,Easy Rules附帶了3種組合規(guī)則的實(shí)現(xiàn):
UnitRuleGroup:單元規(guī)則組是作為一個(gè)單元的復(fù)合規(guī)則:要么應(yīng)用所有規(guī)則珊擂,要么什么都不應(yīng)用圣勒。
ActivationRuleGroup:激活規(guī)則組是一個(gè)復(fù)合規(guī)則,它觸發(fā)第一個(gè)適用規(guī)則摧扇,并忽略組中的其他規(guī)則(XOR邏輯)圣贸。規(guī)則首先按照組內(nèi)的自然順序(默認(rèn)優(yōu)先級(jí))進(jìn)行排序。
ConditionalRuleGroup:條件規(guī)則組是一個(gè)復(fù)合規(guī)則扛稽,其中優(yōu)先級(jí)最高的規(guī)則充當(dāng)條件:如果優(yōu)先級(jí)最高的規(guī)則求值為true吁峻,則觸發(fā)其余規(guī)則。
八.Fizz Buzz
本教程使用簡(jiǎn)單的規(guī)則實(shí)現(xiàn)FizzBuzz應(yīng)用程序在张。FizzBuzz是一個(gè)簡(jiǎn)單的應(yīng)用程序用含,需要從1數(shù)到100,并且:
- 如果數(shù)字是5的倍數(shù)瞧掺,則打印“fizz”;
- 如果數(shù)字是7的倍數(shù)耕餐,請(qǐng)打印“buzz”;
- 如果數(shù)字是5和7的倍數(shù),請(qǐng)打印“fizzbuzz”;
- 否則打印數(shù)字本身.
下面是一個(gè)來(lái)自Java示例的FizzBuzz示例:
public class FizzBuzz {
public static void main(String[] args) {
for(int i = 1; i <= 100; i++) {
if (((i % 5) == 0) && ((i % 7) == 0))
System.out.print("fizzbuzz");
else if ((i % 5) == 0) System.out.print("fizz");
else if ((i % 7) == 0) System.out.print("buzz");
else System.out.print(i);
System.out.println();
}
System.out.println();
}
}
我們將為每個(gè)需求編寫(xiě)一個(gè)規(guī)則:
FizzRule
@Rule(name = "FizzRule", description = "FizzRule description", priority = 1)
public class FizzRule {
@Condition
public boolean isFizz(@Fact("number") Integer number) {
return number % 5 == 0;
}
@Action
public void printFizz() {
System.out.print("fizz");
}
}
BuzzRule
@Rule(name = "BuzzRule", description = "BuzzRule description", priority = 2)
public class BuzzRule {
@Condition
public boolean isBuzz(@Fact("number") Integer number) {
return number % 7 == 0;
}
@Action
public void printBuzz(@Fact("number") Integer number) {
System.out.print("buzz");
}
}
FizzBuzzRule
public class FizzBuzzRule extends UnitRuleGroup {
public FizzBuzzRule(Object... rules) {
for (Object rule : rules) {
addRule(rule);
}
}
@Override
public int getPriority() {
return 0;
}
}
NonFizzBuzzRule
@Rule
public class NonFizzBuzzRule {
@Condition
public boolean isNotFizzNorBuzz(@Fact("number") Integer number) {
// can return true, because this is the latest rule to trigger according to assigned priorities
// and in which case, the number is not fizz nor buzz
return number % 5 != 0 || number % 7 != 0;
}
@Action
public void printInput(@Fact("number") Integer number) {
System.out.print(number);
}
@Priority
public int getPriority() {
return 3;
}
}
FizzBuzzWithEasyRules
public class FizzBuzzWithEasyRules {
public static void main(String[] args) {
// create a rules engine
RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);
// create rules
Rules rules = new Rules();
rules.register(new FizzRule());
rules.register(new BuzzRule());
rules.register(new FizzBuzzRule(new FizzRule(), new BuzzRule()));
rules.register(new NonFizzBuzzRule());
// fire rules
Facts facts = new Facts();
for (int i = 1; i <= 100; i++) {
facts.put("number", i);
fizzBuzzEngine.fire(rules, facts);
System.out.println();
}
}
}
您應(yīng)該得到以下輸出:
1
2
3
4
fizz
6
buzz
8
9
fizz
11
12
13
buzz
fizz
16
17
18
19
fizz
buzz
22
23
24
fizz
26
27
buzz
29
fizz
31
32
33
34
fizzbuzz
36
37
38
39
fizz
41
buzz
43
44
fizz
46
47
48
buzz
fizz
51
52
53
54
fizz
buzz
57
58
59
fizz
61
62
buzz
64
fizz
66
67
68
69
fizzbuzz
71
72
73
74
fizz
76
buzz
78
79
fizz
81
82
83
buzz
fizz
86
87
88
89
fizz
buzz
92
93
94
fizz
96
97
buzz
99
fizz