流れるようなインターフェースって結局ビルダなのね

今更感溢れる話題だけど、Ruby でキーワード引数的な Fluent Interface の実装 - 8時40分が超えられない - subtechの違和感が非常に気になったのでちょっと考えてみた。

まず、違和感のないものを考える

TimeInterval meetingTime = fiveOClock.until(sixOClock);
Martin Fowler's Bliki in Japanese - 流れるようなインターフェース

Ruby脳なので、これは普通すぎて別に流れてないと思った。

  private void makeFluent(Customer customer) {
       customer.newOrder()
               .with(6, "TAL")
               .with(5, "HPK").skippable()
               .with(3, "LGV")
               .priorityRush();
   }
Martin Fowler's Bliki in Japanese - 流れるようなインターフェース

これって、要するにCustomerに追加するOrderビルダのsetterメソッドチェインだよね。

List<Employee> results = jdbcManager.from(Employee.class)
                             .join("department")
                             .where("name like ?", "S%")
                             .orderBy("name")
                             .getResultList();
流れるようなインターフェースと脱CoC - ひがやすを blog

これも、orderByまでを「SQLクエリのビルダ」と考えると自然になる。

違和感のあるものに戻ってくる

fluent = FluentInterface.fluent WEBrick::HTTPServer, :new
server = fluent.DocumentRoot('/var/www').BindAddress('0.0.0.0').Port(8001).execute
server.start
Ruby でキーワード引数的な Fluent Interface の実装 - 8時40分が超えられない - subtech

上記を踏まえると、これもビルダとして考えるのが自然だと思う。
メソッドに対し、特定の引数を固定したメソッドを返す……あれ? これって要するに単なる部分適用?

そう考えると、上記の後に書かれてるメソッド組み立てのほうが違和感は減る。

fluent = FluentInterface.fluent WEBrick::HTTPServer, :new
fluent = fluent.DocumentRoot('/var/www').Port(8001)
fluent = fluent.BindAddress('0.0.0.0')
server = fluent.execute
Ruby でキーワード引数的な Fluent Interface の実装 - 8時40分が超えられない - subtech

1行目でメソッドを取り出し、2〜3行目で部分適用し、その結果のメソッドを再代入し、4行目で実行する*1。うん、自然だ!*2

*1:これがexecuteではなくcallだと更に違和感は減ると思う

*2:でも自分はキーワード引数派です。