Исполняемая модель — это реконструкция модели самого низкого уровня Drools, управляемой двигателем. В текущей серии (до 6.x) исполняемая модель органически развивалась за последние 8 лет и никогда не предназначалась для конечных пользователей. Желающим программно написать правила посоветовали сделать это через генерацию кода и целевой drl; который не был идеальным. Не было никакого способа сделать это более доступным для конечных пользователей, потому что широкое использование анонимных классов в Java было громоздким. С Java 8 и Lambda это меняется, и становится возможной возможность сделать более привлекательную модель, доступную для конечных пользователей.
Эта новая модель генерируется в процессе компиляции языков более высокого уровня, но также может использоваться сама по себе. Цель состоит в том, чтобы эта Исполняемая модель была автономной и избегала необходимости дальнейшего манипулирования байтовым кодом (анализа, преобразования или генерации); С точки зрения этой модели, все обеспечивается либо кодом, либо языковыми уровнями более высокого уровня. Например, индексы и т. Д. Должны предоставляться аргументами, которые язык более высокого уровня генерирует посредством анализа, когда он нацелен на модель Исполнимого файла.
Он предназначен для точного сопоставления с создателями уровней Fluent, используя лямбды Java 8. Это сделает его более привлекательным для разработчиков Java и разработчиков языков. Также это позволит разрабатывать и тестировать низкоуровневые функции движка независимо от языка. Это означает, что мы можем внедрять инновации на уровне движка, не беспокоясь о языковом уровне.
Исполняемая модель должна быть достаточно общей для сопоставления с несколькими доменами. Это будет низкоуровневая модель потока данных, в которой вы можете обращаться к моделям функционально-реактивного программирования, но все же ее можно будет использовать для построения системы на основе правил.
В следующем примере показано первое представление беглого DSL, использованного для построения исполняемой модели:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
DataSource persons = sourceOf(new Person("Mark", 37), new Person("Edson", 35), new Person("Mario", 40)); Variable<Person> markV = bind(typeOf(Person.class));Rule rule = rule("Print age of persons named Mark") .view( input(markV, () -> persons), expr(markV, person -> person.getName().equals("Mark")) ) .then( on(markV).execute(mark -> System.out.println(mark.getAge()) )); |
Предыдущий код определяет DataSource, содержащий несколько экземпляров person, и объявляет переменную markV типа Person. Само правило состоит из двух обычных частей: LHS определяется набором входных данных и выражений, передаваемых методу view (), а RHS — это действие, определяемое лямбда-выражением, передаваемым методу then ().
Анализируя LHS более подробно, утверждение:
|
1
|
input(markV, () -> persons) |
связывает объекты из источника данных Persons с переменной markV, сопоставляя шаблон с классом объекта. В этом смысле источник данных можно рассматривать как эквивалент точки входа Drools.
Наоборот выражение:
|
1
|
expr(markV, person -> person.getName().equals("Mark")) |
использует Predicate для определения условия, которому должен соответствовать объект, связанный с переменной markV, для того, чтобы ядро успешно соответствовало ему. Обратите внимание, что, как и ожидалось, оценка сопоставления с образцом не выполняется ограничением, сгенерированным в результате какого-либо анализа или процесса компиляции, а просто выполняется путем применения лямбда-выражения, реализующего предикат (в данном случае person — > person.getName (). equals («Mark»)) к сопоставляемому объекту. Другими словами, первый DSL создает исполняемую модель правила, эквивалентную модели, полученной в результате анализа следующего drl.
|
1
2
3
4
5
6
|
rule "Print age of persons named Mark"when markV : Person( name == "Mark" ) from entry-point "persons"then System.out.println(markV.getAge());end |
Он также находится в стадии разработки и может быть скорректирован по правилам, определенным этим DSL. В частности, можно добавить эти правила в CanonicalKieBase, а затем создать из него KieSessions, как и для любого другого обычного KieBase.
|
1
2
3
4
5
|
CanonicalKieBase kieBase = new CanonicalKieBase();kieBase.addRules(rule);KieSession ksession = kieBase.newKieSession();ksession.fireAllRules(); |
Конечно, DSL также позволяет определять более сложные условия, такие как соединения:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
Variable<Person> markV = bind(typeOf(Person.class));Variable<Person> olderV = bind(typeOf(Person.class));Rule rule = rule("Find persons older than Mark") .view( input(markV, () -> persons), input(olderV, () -> persons), expr(markV, mark -> mark.getName().equals("Mark")), expr(olderV, markV, (older, mark) -> older.getAge() > mark.getAge()) ) .then( on(olderV, markV) .execute((p1, p2) -> System.out.println(p1.getName() + " is older than " + p2.getName()) )); |
или экзистенциальные модели:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
Variable<Person> oldestV = bind(typeOf(Person.class));Variable<Person> otherV = bind(typeOf(Person.class));Rule rule = rule("Find oldest person") .view( input(oldestV, () -> persons), input(otherV, () -> persons), not(otherV, oldestV, (p1, p2) -> p1.getAge() > p2.getAge()) ) .then( on(oldestV) .execute(p -> System.out.println("Oldest person is " + p.getName()) )); |
Здесь not () обозначает отрицание любого выражения, поэтому используемая выше форма на самом деле является всего лишь ярлыком для:
|
1
|
not( expr( otherV, oldestV, (p1, p2) -> p1.getAge() > p2.getAge() ) ) |
Также накопление уже поддерживается в следующей форме:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
Variable<Person> person = bind(typeOf(Person.class));Variable<Integer> resultSum = bind(typeOf(Integer.class));Variable<Double> resultAvg = bind(typeOf(Double.class));Rule rule = rule("Calculate sum and avg of all persons having a name starting with M") .view( input(person, () -> persons), accumulate(expr(person, p -> p.getName().startsWith("M")), sum(Person::getAge).as(resultSum), avg(Person::getAge).as(resultAvg)) ) .then( on(resultSum, resultAvg) .execute((sum, avg) -> result.value = "total = " + sum + "; average = " + avg)); |
Чтобы предоставить еще один более полный вариант использования, с помощью этого DSL можно определить исполняемую модель классического примера пожарной тревоги и сигнализации следующим образом.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
Variable<Room> room = any(Room.class);Variable<Fire> fire = any(Fire.class);Variable<Sprinkler> sprinkler = any(Sprinkler.class);Variable<Alarm> alarm = any(Alarm.class);Rule r1 = rule("When there is a fire turn on the sprinkler") .view( input(fire), input(sprinkler), expr(sprinkler, s -> !s.isOn()), expr(sprinkler, fire, (s, f) -> s.getRoom().equals(f.getRoom())) ) .then( on(sprinkler) .execute(s -> { System.out.println("Turn on the sprinkler for room " + s.getRoom().getName()); s.setOn(true); }) .update(sprinkler, "on"));Rule r2 = rule("When the fire is gone turn off the sprinkler") .view( input(sprinkler), expr(sprinkler, Sprinkler::isOn), input(fire), not(fire, sprinkler, (f, s) -> f.getRoom().equals(s.getRoom())) ) .then( on(sprinkler) .execute(s -> { System.out.println("Turn off the sprinkler for room " + s.getRoom().getName()); s.setOn(false); }) .update(sprinkler, "on"));Rule r3 = rule("Raise the alarm when we have one or more fires") .view( input(fire), exists(fire) ) .then( execute(() -> System.out.println("Raise the alarm")) .insert(() -> new Alarm()));Rule r4 = rule("Lower the alarm when all the fires have gone") .view( input(fire), not(fire), input(alarm) ) .then( execute(() -> System.out.println("Lower the alarm")) .delete(alarm));Rule r5 = rule("Status output when things are ok") .view( input(alarm), not(alarm), input(sprinkler), not(sprinkler, Sprinkler::isOn) ) .then( execute(() -> System.out.println("Everything is ok")));CanonicalKieBase kieBase = new CanonicalKieBase();kieBase.addRules(r1, r2, r3, r4, r5);KieSession ksession = kieBase.newKieSession();// phase 1Room room1 = new Room("Room 1");ksession.insert(room1);FactHandle fireFact1 = ksession.insert(new Fire(room1));ksession.fireAllRules();// phase 2Sprinkler sprinkler1 = new Sprinkler(room1);ksession.insert(sprinkler1);ksession.fireAllRules();assertTrue(sprinkler1.isOn());// phase 3ksession.delete(fireFact1);ksession.fireAllRules(); |
В этом примере можно отметить еще несколько вещей:
- Некоторые повторения необходимы для привязки параметров выражения к формальным параметрам лямбда-выражения, оценивающего его. Надеюсь, что эту проблему JDK удастся преодолеть, используя аргумент компиляции -parameters.
- any (Room.class) — это ярлык для связывания (typeOf (Room.class))
- Входные данные не объявляют источник данных. Это ярлык для указания того, что эти объекты получены из пустого источника данных по умолчанию (соответствует точке входа по умолчанию Drools). Фактически в этом примере факты программно вставляются в KieSession.
- Использование входа без предоставления какого-либо выражения для этого входа на самом деле является ярлыком для входа (аварийный сигнал), expr (аварийный сигнал, a -> true)
- Таким же образом экзистенциальный шаблон без каких-либо условий, таких как not (огонь), является еще одним ярлыком для not (expr (fire, f -> true))
- Синтаксис Java 8 также позволяет определять предикат как ссылку на метод, обращаясь к логическому свойству факта, подобного expr (sprinkler, Sprinkler :: isOn)
- RHS вместе с блоком кода, который должен быть выполнен, также обеспечивает свободный интерфейс для определения действий с рабочей памятью (вставки / обновления / удаления), которые должны выполняться при запуске правила. В частности, обновление также получает переменные Strings, сообщающие имя свойств, измененных в обновленном факте, как в обновлении (sprinkler, «on»). Еще раз, эта информация должна быть явно предоставлена, потому что исполняемая модель должна создаваться без необходимости какого-либо анализа кода.
| Ссылка: | Исполняемая модель Drools от нашего партнера JCG Марио Фуско в блоге Drools & jBPM . |