はじめに
Drools では、DRL (Drools Rule Language) 構文で記述したルールによって、入力データが特定の条件をみたしたときの処理を自動化できます。
以下は、人の年齢に応じてドリンクを決定するルールです。
rule "Child"
when
$person : Person( age < 20 )
$drink : Drink()
then
$drink.setName( "Orange Juice" );
$drink.setCharge( 100 );
end
rule "Adult"
when
$person : Person( age >= 20 )
$drink : Drink()
then
$drink.setName( "Beer" );
$drink.setCharge( 200 );
end
when 節では入力データに関する条件が、then 節では条件をみたしたときに実行する処理が記述されています。
プログラミング言語にある程度慣れている人であれば、おおよそどんなことをやっているか判断できるでしょう。
しかし、プログラミング経験がない方にとっては、DRL を一目で理解することは難しいと思われます。
今回の記事では Drools の DSL 機能を使って、Drools のルールをよりわかりやすくする方法を紹介します。
DroolsのDSL機能について
DSL (Domain Specific Languages) は、日本語ではドメイン固有言語、またはドメイン特化言語などと呼ばれます。
Java などのプログラミング言語と異なり、特定の目的を果たすことにフォーカスして設計される言語を意味します。
アプリの依存関係を定義する目的で利用される、Gradleのビルドスクリプト等が例として挙げられます。
Droolsでは、ルールの記述に用いるDSLをDSLファイルに、DSLを用いたルール定義をDSLRというファイルで記述できます。
DSL・DSLRファイルをDroolsに読み込ませると、内部的にDRLファイルに変換したうえで、ルールを実行することができます。
DSLファイルは、次のような形式で記述されます。
[when]{ルールの条件を表すフレーズ} = {DRL構文}
[then]{ルールのアクションを表すフレーズ} = {DRL構文}
ここで定義している{ルールの条件を表すフレーズ}・{ルールのアクションを表すフレーズ}を、DSLRファイルで利用することができます。
定義したフレーズが、プログラマーでない方にとってもわかりやすい表現になっていれば、ルールの可読性も向上することが期待されます。
サンプルアプリ
例として、最初に紹介したドリンク判定のルールをDSLで書いて動かしてみます。
サンプルコードのプロジェクトはGithubにあげています。
環境、ライブラリバージョン
- windows11
- openjdk 17
- Drools 8.44.0.Final
データクラス
通常のDRLでルールを実行する場合と同様、データクラスを定義します。
public record Person(
String name,
int age
){}
public class Drink {
private String name;
private int charge;
public String getName() {
return name;
}
public int getCharge(){
return charge;
}
public void setName(String name) {
this.name = name;
}
public void setCharge(int charge){
this.charge = charge;
}
@Override
public String toString() {
return "name: " + name + ", " + "charge: " + charge;
}
}
Personクラスはイミュータブルでいいので、手軽にレコードクラスで書いてます。
DSLファイル
条件、アクション部に用いるDSLフレーズを定義します。
[when]来店客の年齢が {age} 才未満 = $person : Person(age < {age})
[when]来店客の年齢が {age} 才以上 = $person : Person(age >= {age})
[when]ドリンクが注文される = $drink : Drink()
[then]"{name}"を提供する = $drink.setName( "{name}" );
[then]料金は {charge} 円 = $drink.setCharge( {charge} );
{age}
, {name}
などと書いている箇所で、DSLRに記載した値を受け取ることができます。
DSLRファイル
DSLファイルに定義したフレーズを使って、ルールを記述していきます。
import org.example.dslsample.Person
import org.example.dslsample.Drink
expander DrinkRule.dsl
rule "Child"
when
来店客の年齢が 20 才未満
ドリンクが注文される
then
"オレンジジュース"を提供する
料金は 100 円
end
rule "Adult"
when
来店客の年齢が 20 才以上
ドリンクが注文される
then
"ビール"を提供する
料金は 200 円
end
DSLRをDRLへ変換するために、expander
属性でDSLファイル名を指定しています。
動作確認
実際にルールを動かしてみます。
以下がテストコードの一部です。
呼び出し部分のJavaコードは、通常のDRLで記載したルールを動かす場合と変わりません。
public class SampleDslTest {
@Test
public void test_ドリンク判定ルール_Child() {
KieServices ks = KieServices.Factory.get();
KieContainer kieContainer = ks.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession();
// set up
var person = new Person("太郎", 13);
var drink = new Drink();
kieSession.insert(person);
kieSession.insert(drink);
// execute
kieSession.fireAllRules();
// assert
assertEquals("オレンジジュース", drink.getName());
assertEquals(100, drink.getCharge());
kieSession.dispose();
}
...
テストを実行すると、今回は13歳の太郎君が来店したので、飲み物はオレンジジュースと判定されることがわかります。
import文などプログラムチックなものは残っているものの、かなりルールの可読性が向上したと感じます。
今回紹介したDSL機能は、業務担当者(※)と協力して、アプリに組み込むルールを作成する際に有用ではないかと思われます。
(※)ここではIT職以外の方で、特定の業務知識をもち、作成したアプリケーションで日々の業務を行う人を想定しています
業務担当者とルールを検討する、実装したルールをレビューしてもらう、といったことがDSL機能を使うことでやりやすくなるのでは、と考えています。
参考
Drools 公式ドキュメント
通常のDRLを使ったルールの実行方法については、こちらもご参照ください