Martin Fowler氏のDSL本では、DSLコード自体より、意味モデル(Semantic Model)の重要性を繰り返し述べている。それは、「Hence the DSL is merely a thin facade over the model.(ゆえに、DSLはモデル上の薄いファサードに過ぎない)」といった文章にも現れている。
ちなみに、DSL本では、意味モデルは以下のように定義されている。
The domain model that’s populated by a DSL
(意味モデルは)DSLによって生成されるドメイン・モデル
Semantic Model is realy just a Domain Model that is populated by the DSL. As with any Domain Model it contains the heart of the behavior for the domain. The the Semantic Model of a DSL is usually a subset of the overal Domain Model for an application. From the Domain Model’s point of view, the DSL is just a fancy alternative way of creating its objects and hooking them together. From the DSL’s point of view the Semantic Model is the output of the overall parsing operation.
意味モデルは単に、DSLによって生成されるドメイン・モデルでしかない。いかなるドメイン・モデルとも同様に、意味モデルは、ドメインの中心となる振る舞いを含んでいる。DSLの意味モデルは、通常、アプリケーションにおける全ドメイン・モデルのサブセットとなる。ドメイン・モデルの観点から、DSLは単にドメイン・モデルのオブジェクトを生成し、結びつける、風変わりな代替手段に過ぎない。DSLの観点からは、意味モデルは全パース処理の出力結果となる。
つまり、アプリケーションとしての本質は(ドメイン・モデルのサブセットである)意味モデルであり、DSLはそれを生成するための「手段の1つ」に過ぎないということである。
以下、サンプルのアプリケーションを用いて、意味モデルとDSLの関係について考えてみる。
サンプル・アプリケーションの概要
サンプルとして、クリティカル・パスを算出するアプリケーションを用いる。
クリティカル・パスとは、プロジェクトにおける各タスクの所要時間と、タスク間の関連が定義された場合に算出できる、「プロジェクト完了までにかかる最長の経路」である。
例えば、SlideShareで公開されている例を見てみる。プロジェクトにおける各タスクとタスク間の関係は、以下のとおり定義されている。
タスク | 期間 | 先行タスク |
---|---|---|
A | 3 | - |
B | 4 | A |
C | 3 | B |
D | 10 | B |
E | 8 | D |
F | 4 | D |
G | 6 | D |
H | 8 | C,E,F,G |
I | 5 | H |
J | 5 | H |
K | 4 | I |
L | 2 | J |
M | 4 | K,L |
上記から算出される、このプロジェクトにおけるクリティカル・パスは、A→B→D→E→H→I→K→Mとなる(算出アルゴリズムは、SlideShareを参照)。
当然ながら、クリティカル・パスを求めるアプリケーションに、DSLは必須ではない。しかしながら、用途によっては、DSLの実装が望ましい場合がある。例えば、プロジェクトにおいてクリティカル・パスが必要になるのは、通常、プロジェクト・マネージャーである。よって、彼のためにDSLを準備してあげるのは、ありうる話である。
DSLのアプリケーションを作成するための順序は、以下のようにするのが一般的だろう。*1
- 問題領域に対する、適切なモデルを考える
- モデルを生成するDSLを定義する
つまり、重要な意味モデルの検討からはじめ、その後、その意味モデルに対するDSLを定義する。今回のサンプルでも、この順番で検討する。
1. 問題領域に対する、適切なモデルを考える
まずは、この問題を解くためのモデルを考える。この問題領域で扱うのは、スケジュールである。上の図で表現されているとおり、この場合、有向グラフとして扱うのが適切である。つまり、意味モデル(各プロジェクトのスケジュールを表すモデル)は、例えばNodeクラスや、Edgeクラスなどのオブジェクトの関連として、表されることとなる。
ちなみに、DSL本では、アプリケーションの構造を表すモデル(NodeクラスやEdgeクラスなど)を適用モデル(Adaptive Model)、具体的な個々のアプリケーションを、オブジェクト間の関連として表すモデルを、意味モデルと呼んでいる。
後は、この意味モデル上で、クリティカル・パスを計算するアルゴリズムを、コードとして記述するだけである。ここまでで、アプリケーションのコア(スケジュールをモデルとして表し、クリティカル・パスを算出する)は、ほぼ完成したことになる。
2. モデルを生成するDSLを定義する
次に、どのようなDSLを定義するかを検討する。DSLの設計において重要なのは、「誰がそのDSLを使うか」である。その人にとって、そのアプリケーションがどう見えたら自然か、を考えてDSLを定義する。
今回のサンプルでは、例えば以下のような形で、各タスクを定義するDSLを考えることができる。
task name: A duration: 3 end task name: B duration: 4 predecessors: A end ...
あとは、このDSLを解釈し、意味モデルを生成するパーサーを作れば、アプリケーションは完成である。parser generatorを使う場合、DSLを表す文法ファイルを定義し、パーサーを記述することになる。
もう1つのDSL
先ほどのDSLの文法は、プロジェクト・マネージャーにとって、わかりやすいものでは無いかもしれない。例えば、以下のようなDSLコードで、表形式でタスクを定義する方が、わかりやすいかもしれない。
#ID time predecessors |A |3 |- | |B |4 |A | |C |3 |B | |D |10 |B | |E |8 |D | |F |4 |D | |G |6 |D | |H |8 |C,E,F,G | |I |5 |H | |J |5 |H | |K |4 |I | |L |2 |J | |M |4 |K,L |
このDSLの変更において、意味モデルの変更は一切不要である。必要なのは、DSLから意味モデルを生成するパーサーのみである。つまりアプリケーションの本質である意味モデルは、DSLコードをどのような記法にするかには左右されない。DSLは、意味モデルを生成するための、一手段である。これが、「DSLはモデル上の薄いファサードに過ぎない」という言葉の意味である。
また、このようなことができるのも、意味モデルをしっかりと定義しておいたからである。アプリケーションのコア(意味モデル)と、インターフェース(DSLコード)の部分を明確に分けることで、アプリケーションの変更容易性を高めることができる。
まとめ
アプリケーションのコアは、あくまでも意味モデルであり、DSLは、意味モデルへのインターフェースである。インターフェースであるので、用途に応じて変更が可能となる。
ただし、これはDSLがつまらないもの、ということではないことには留意する必要がある。画面UIと同様、DSLも、アプリケーションの使い勝手を決めうる、大事な要素である。
*1:もちろん、意味モデルからではなく、まずDSLのサンプルを作ってみる、という方法もあり。