DSL / Method Chaining


DSL

Method Chaining (メソッドチェーン)

一言要約

状態変更メソッドが自身のオブジェクト(this)を返すことによって、1つの命令文中で複数の状態変更メソッドを呼び出せるようにする。

要約

  • 内部DSLの基本は、Expression Builderなどのオブジェクトによって提供される流れるAPI。Method Chainingはそれを実現するイディオムの1つ。

仕組み

  • セッターメソッド(setSpeed())を修正して、値をセット後に自分自身を返すようにする(speed())。
    • バートランド・メイヤーのコマンド・問い合わせ分離原則(Command Query Separation)に反するが、流れるインタフェースがもっと読みやすくなる。
    • ただし、メソッドチェーンは式ビルダ(Expression Builder)の中に限定して使うことを強くオススメする。
  • メソッドチェーンはソースフォーマットの慣習にも変更を促す。
    • メソッド呼び出しのつながりを階層的にフォーマットする。
    • メソッド呼び出しのピリオド「.」は行の初めに置くのが一般的。
      • ただし、Rubyの場合は文法的に「.」を行末に書かないとダメ。
    • メソッドチェーンを階層的にフォーマットするとデバッグもしやすい。
  • メソッドチェーンに階層構造がある場合はコンテキスト変数(Context Variable)を導入する必要がある。

 終了問題(Finishing Problem)

  • メソッドチェーンには、いつが終了なのか分からないという問題がある。チェーンの最後に何らかの処理をしなければいけない場合などに問題となる。
  • 解決法の1つは終了マーカー(done()など)を導入すること。イマイチな点は以下の2つ。
    1. DSLにノイズが入る。
    2. ビルダがコンテキストを知る必要がある(コンテキスト変数が必要)。
  • もっと良い解決法は入れ子関数(Nested Function)を使うこと。

 漸進的インタフェース(Progressive Interfaces)

  • 複数のインタフェースを使い分けることで、DSLの進行に応じて呼び出せるメソッドを限定していく方法。

 文法についての考察(Thinking in Terms of Grammars)

???

いつ使うべきか

  • メソッドチェーンは、内部DSLを使う場合はほぼ必須。ただし、他の手法と一緒に使うのがベスト。
  • 文法的には、以下のBNFで表される文法を作るときに最適。
    parent ::= (this | that)*
    • 以下のような文法を扱う場合は、漸進的インタフェースでもできるが、入れ子関数(Nested Function)を使った方がいいと思う。
      parent ::= first second
  • 最大の問題は終了問題

 例1:Javaによる単純なコンピュータの構成

  • 1つのプロセッサ(Processor)と複数のディスク(Disk)からなるコンピュータ(Computer)を構成する内部DSL
    • コンテキスト変数(currentProcessor、currentDisk)と終了メソッド(end())を使っている。
ex1s.png

 例2:C#のプロパティを使ったチェーン

  • C#ではプロパティを使ってメソッドチェーンを実現できる。
    • ただし、ゲッター(get { ... })で状態変化するコードを書かなければならないので、気持ち悪い。
    • メソッドチェーンの使用はあくまで式ビルダ>DSL?内だけに留めること。

 例3:C#による漸進的インタフェース

  • メッセージビルダ(MessageBuilder?)によってメールのメッセージ(Message)を構築する内部DSL
ex3s.png

 例4:C#による終了問題

  • カレンダー(Calendar)に予定(Appointment)を追加する内部DSL
  • 素朴な実装方法には以下2つの問題がある。
    1. 終了メソッド(Done())はDSLにとってノイズ。
    2. 予定ビルダ(AppointmentBuilder?)がカレンダー専用になってしまっている。
ex4s.png
  • クロージャを使うことで2つ目の問題は解決できる。
ex4s_closure.png
  • 1つ目の問題を解決するには、入れ子関数を使う。
    cal.Add(Appointment.Build(..)..);

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

  • ファウラー先生、「終了問題」の言葉が "Finishing Problem" と "Stopping Problem" で揺れています。
  • "Example: The stopping problem (C#)" のソースコードで「Appointment」クラスと「AppointmentBuilder?」クラスの表記が統一されていない。
  • Closureを導入することによる解決策では、CalendarがAppointmentBuilder?に依存することになってしまうので、この方が問題なのでは?

担当者のつぶやき

  • まだまだ原稿の完成度は低いですね。今回第1稿が完成したらしいですが、本当にこれから完成度が高まっていくんでしょうか? まだまだ修正するところが多そう。

みんなの突っ込み