DSL / Using Functions


DSL

関数の利用

一言要約

関数を利用してDSLを構築する際には「メソッドチェーン」「関数シーケンス」「ネスト関数」などのパターンを使うが、これらは相互に排他的ではなく、混合して使うことができる。ただし、最適なブレンドのためには状況に応じた意思決定が必要となる。

要約

コマンド-クエリインタフェースとDSLとの違いは、関数同士の結合方法。

  • メソッドチェーン
  • 関数シーケンス
  • ネスト関数

関数シーケンス

 computer();
   processor();
     cores(2);
     processorType(i386);
   disk();
     diskSize(150);
   disk();
     diskSize(75);
     diskSpeed(7200);
     diskInterface(SATA);
  • グローバル関数
    • 関数スコープを解決するにはグローバル関数を使うのが近道だが、言語処理層からだけでなく、モデルからも可視になるのは問題。名前空間やstaticインポートで軽減可能。
  • グローバル変数
    • 現在地を追跡するために、コンテキスト変数が必要になり、関数がグローバルな場合は変数もグローバルに
  • オブジェクトスコープを使うことで、グローバル関数/変数の問題を解決可能
    • 大抵の場合、DSLはビルダのサブクラスに記述することになる

メソッドチェーン

 computer()
   .processor()
     .cores(2)
     .i386()
   .disk()
     .size(150)
   .disk()
     .size(75)
     .speed(7200)
     .sata()
   .end();
  • グローバルの問題の多くを解決
    • イクスプレッションビルダ上にコンテキスト変数を置くことができるため

ネスト関数

   computer(
     processor(
       cores(2),
       Processor.Type.i386
     ),
     disk(
       size(150)
     ),
     disk(
       size(75),
       speed(7200),
       Disk.Interface.SATA
     )
   );
  • 自然に階層構造を表現できる。構築対象が階層構造を持つ場合に特に有利
    • ビルダオブジェクトではなく、モデルオブジェクトを返せることも利点
  • 関数シーケンス/メソッドチェーンと異なり、コンテキスト変数自体を不要にできることがある
  • 評価順序に特徴があるため、注意が必要
    • o(i(e(i(e()))))の問題
  • カッコが多いなど、ノイジーになりがち
  • ネスト関数と同様、修飾なしの関数を使うことになる。これは、他のパターンと同様にオブジェクトスコープで解決できる。
  • 名前つき引数がないと、読みづらくなりがち
    • リテラルマップで対処するなど

ハイブリッド

 computer(
   processor()
     .cores(2)
     .type(i386),
   disk()
     .size(150),
   disk()
     .size(75)
     .speed(7200)
     .iface(SATA)
   );
  computer(
    processor()
     .cores(4)
  );
  • 適材適所
  • だが、記法上混乱しがち
    • 区切りのルールがパターンによって異なるため
  • 適したブレンドは、状況に応じて決めるべき

ファウラーへのフィードバック

担当者のつぶやき

みんなの突っ込み