2009年06月14日

なぜDSLを採用するのか?

DSLとは、Domain-Specific Languageの略です。
ドメイン特化言語、またはドメイン固有言語などと訳されます。
特定の問題領域のみに焦点をあてて設計された言語をさします。
オフィス製品によくあるマクロや、正規表現などがいい例です。

 マクロや正規表現などのように、なにげなくDSLを利用する場面は多いのですが、自分でDSLを設計、実装するとなると、積極的に行われることは少ないようです。
多くの場合、DSLを採用すれば効果的な場面が、見過ごされているのではないでしょうか。
「今こそ、DSLが最適な解だ!」というインスピレーションが生まれないのは、DSLが実際どんなことを解決してくれるのかについて、あまり理解されていないことが原因なのではないかと思います。
そこで、今回は一例として、私が実際にDSLを採用した際の動機について述べてみたいと思います。
 DSLの詳細については、wikipediaなどを参照してください。
Martin Fowlerも、いい解説(日本語訳)をされています。

 最近、私は帳票出力システムの一部をリプレイスしました。
必要なデータをデータベースから取得し、帳票出力用にデータを加工し、出力するわけです。

 帳票は、既に7種類ほど定義されていて、それぞれが同じデータに対する違ったビューを提供しています。
具体的にいえば、納品書や請求書などです。
 実際の出力先は、PDFやFAXなのですが、PDF生成やFAX送信のシステムは別にあり、それらが求めるデータオブジェクトを生成して渡してあげることが、こっちの仕事です。
FAXについては、事前に定義されたCSVフォーマットに従ってデータを渡します。
帳票のレイアウト自体は、PDF生成システムが提供している仕組みがあり、そのシステム独自のフォーマットで定義されています。
ただし、あまり使いやすい仕組みとは言えず、機能も不十分なものです。
 レイアウト定義を操作するためのGUI製品が提供されていますが、機能が貧弱で、誰がやってもイイカンジの確率で間違いを混入してしまうようなしろものです。
レイアウト定義に簡単な式を含めることもできるのですが、この場合、それは避けておいたほうがよさそうです。
できるだけレイアウト定義を単純にしておくことで、メンテナンスの負担を軽減し、ミスの混入機会を抑えるべきです。

 さて、この状況で、私は何をリプレイスしたのでしょうか?
  • データはデータベースにすべて揃っている
  • 帳票のレイアウトを定義したものは用意されている
  • 最終的な出力を行うシステムも、用意されている
開発チームは、何らかの問題があることを認識していました。
しかし、わざわざリプレイスしなければならないほどの、何があったというのでしょうか?
それは、データのマッピングと加工、そして出力先のシステムが求めるデータ形式への変換です。

 リプレイス前のシステムでは、7種類の帳票のためのデータマッピングと加工のためのコードがすべてストレートにコード化されていました。
そのため、処理中の帳票の種類はどれか?フラグを表すデータ項目の値が何か?という条件分岐がいたるところにあらわれていました。
複数の帳票に共通する出力項目については、ロジックが再利用されていましたが、それは実際に1つのコードが共有されている箇所もあれば、コピー&ペースト(!)戦略によって再利用されている箇所もありました。

 そんなわけで、このシステムでは、帳票の出力内容にちょっとした変更を加えるたびに、理不尽な苦労を強いられていました。
他の帳票の同じ項目に、変更の影響があるのかないのかを予測することが困難だからです。
すべての帳票を再出力し、目で確認するのが一番手っ取り早いというありさまだったのです。
そうでなければ、アヤシイ箇所のコードを慎重に目で追うのです。目くるめくスパゲッティコードの中を。

 これをリプレイスするにあたって、目標は必然的に定まりました。
  • いくつもの種類がある帳票の、すべての出力内容に対応できる
  • できる限り条件分岐や重複コードを無くす
  • 帳票の種類の増減が簡単にできるようにする
  • 出力内容の変更が簡単にできるようにする

 これらは、適切なソリューションの一つとして、DSLの構築という選択肢が有効であることを表しています。
その理由として、以下のようなことを挙げることができます。
  • 「帳票の出力定義」という、明確なドメインを持っている。
    DSLはドメイン特化言語であるので、問題領域が明確でないと、セマンティクスが曖昧になり、扱いづらくなってしまいます。
    それに対して、今回のドメインは非常に明確です。
  • 簡単に変更できる必要がある
    「帳票」とは、紙メディア(今回の場合、PDFだったりもしますが)上のユーザインタフェイスです。
    ディスプレイ上のユーザインタフェイスと同様に、非常に変化しやすい部分です。
    法改正、ビジネスの変化、あるいはユーザの好みなど、さまざまな理由で変化しますが、いつ、どのように変化するのかはほとんど予測不能です。
    実際、既存のシステムも、そのような度重なる変化によって、かなりのLava Flowを含んでいました。
    現時点でどれだけ柔軟なシステムであっても、変更しづらければ結局既存システムと同じ運命を辿ります。
  • データベース項目と帳票の出力項目の単純なマッピングだけでなく、データの加工方法まで定義できる必要がある
    帳票の出力内容が変化する場合、項目自体が増えたり減ったりという変化もよくありますが、どのデータを使用するかとか、どう編集するかということが変化することもよくあります。
    つまり、変更に強い「帳票出力定義」システムを作る場合には、データの加工方法まで含めて変更に強くなっていなければ、あまり意味がないということになります。

 そんなわけで、私はデータバインド指定と、文字列加工、演算子、および集合関数をもった、シンプルなDSLを作りました。
このDSLでは、データを取得したり、加工したりする際の具体的な実装は隠蔽されています。
「どこのデータをどう加工して、どこに出力するのか」という、ドメイン依存の抽象度の高い言語になっています。

これらの式は、実行時にパースされ、Javaオブジェクトのコンポジット構造をビルドします。
これは、いくらかの経験があれば、それほど難しくはありません。
XMLパーサを利用したことがあれば、XML以外のパーサを自分で作るにはどうすればいいのか、だいたい想像はつくはずです。
DI(IoC)コンテナを利用したか、あるいは作ったことがあれば、パーサによってDIすれば、あらゆる式を柔軟に処理できることも、なんら不思議ではないでしょう。

 このように、DSLを採用すると、コードはDSL自体の処理に終始します。
帳票にはどんな種類があるのかとか、どんな出力項目があるのかとかいう依存度の高い情報は局所化され、非常に変化に強い、柔軟なシステムになります。
おまけとして、DSLをソース中でなく、外部のファイルに置いておけば、ごく簡単な変更なら、コンパイルすら不要にすることができます。
posted by craftsman at 05:37 | 東京 ☁ | Comment(0) | TrackBack(0) | 発展編
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/121450001
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。