流れるようなinterface

最近、Martin Fowler先生のBlogの存在を知り、しかも日本語訳も大量に公開されていることを知った。それを毎日少しずつ読み進める(というより気になった記事から読んでいく)のが現在の僕の唯一娯楽です何か悪いか?

Martin Fowler's Bliki (Blog本家)
日本語翻訳wiki

面白い記事はたくさんあるのだけど、その中から「流れるようなinterface」という記事について。

外部に公開して利用してもらう手続き(interface/API)は、たとえ時間をかけてでも以下の条件を満たすよう設計すべきであるという主張。


・手続きの名称が適切で、それを利用したコードの可読性が高いこと
・利用者への要求が可能な限り少ないこと


そのひとつのあり方が「流れるようなinterface」だ。

このAPIは読みやすさを第一に設計されている。流れるようにするには、設計とAPIの構築に時間がかかるという代償をともなう。コンストラクタ、セッター、addXXXメソッドといったシンプルなAPIは簡単に書くことができるが、ナイスで流れるようなAPIにたどり着くには、それなりの長考が必要だ。
(中略)流れるようなAPIについてもっと考えてみたいのであれば、JMockのコードを見てみるといいだろう。 JMockなどのモックライブラリは、振舞の複雑なスペック(仕様)を作る必要がある。ここ数年で様々なモックライブラリが作られてきたが、JMockには非常にナイスで流れるようなAPIが含まれており、それが正に流れるが如くなのである。以下にエクスペクテーション(期待)の例を挙げる。

mock.expects(once()).method("m").with( or(stringContains("hello"),
                                          stringContains("howdy")) );

流れるようなinterface

引用したコードを見ると、まさに流れるような。JMockが何者かは知ったことではないが、何をしようとしているのかは一見して推測することができる。このような設計をするための典型的なテクニックが、セッターで値を返すようにするというものだ。一番単純な例ではjavaの StringBuffer.append() などがこの例にあたるか。


興味深いことに上で例示されたjavaのコードは昨日の記事で引用したschemeのコードによく似ている。

■schemeの実装例(『計算機プログラムの構造と解釈』p.67)

(define (sum-odd-squares tree)
  (accumulate +
              0
              (map square
                   (filter odd?
                           (enumerate-tree tree)))))


最初に引用したjavaのコードは、セッターで値が返る設計であるため、関数型言語に近い構造になったということは言えると思う。それから、セッターとは関係ないが、ObjectCompositionを駆使して設計されている、つまりObject間の委譲関係が適切であるコードは、確かに流れるような形状をしていることが多い(その対局がナイアガラの滝のような巨大な手続きで、あの手のコードを真剣に読んでいると本当に体調が悪くなってくる)。

とにかく、interfaceの設計を正しく行ったオブジェクト指向のプログラムは、非常に読みやすく、部品の入れ替えも容易であることがよくわかる。そもそも、interfaceを定めることはそのObjectの役割を明確にすることであり、プロセスや問題領域の抽象化が充分に行われた結果である可能性が高い。混沌とした領域にinterfaceを定めることなどできるはずがない。混沌には他我もhogeもhugaもないんだから。そして、部品入替えが容易なプログラムはユニットテストを行いやすく、機能追加によるバグに強いことは言うまでもない。

さらに、Seasar2の例


Seasar2の作者、ひがやすおさんが「流れるinterface」について力説されていたので引用させていただく。

流れるようなinterfaceとメソッドチェーンは違うもの

流れるようなインターフェースは、うまく実装できれば、Javaのような静的言語と相性が良い。コード補完によって、流れがさらに滑らかになるから。

確かにこれは静的型付言語の強みかもしれない。

流れるようなインターフェースでは、ソースコードを書いている人が、中断することなく流れるようにコーディングできなければいけない。

うん、流れるようなinterfaceとただのメソッドチェーンは違うんだな。
そのことがよくわかる対比が以下の記事に載っていました。Seasar2Hibernateの属性注入プロセスを例に挙げています。

S2JDBCの概要

S2JDBCの流れるインターフェースを使った例

List<Employee> results = jdbcManager.from(Employee.class)
                             .join("department")
                             .where("id in (? , ?)", 11, 22)
                             .orderBy("name")
                             .getResultList();

Hibernateのcriteriaを使った例(メソッドチェーン)

List<Emploee> results = (List<Employee>) session.createCriteria(Employee.class)
                             .add(Restriction.eq("id"), new Integer(11))
                             .add(Restriction.eq("id"), new Integer(22))
                             .addOrder(Property.forName("name").asc())
                             .setFetchMode("department", FetchMode.EAGER)
                             .list();

Seasar2jdbc、美しい。