じゃ、パイプラインドイテレータ、と呼ぶか?

最初に、たちの悪いエイプリールフールネタと思われる前に、懺悔のために CoR という私の解釈が不適切だった理由を書く必要があります。Chain-of-Responsibility とは「ある同じインターフェイスを持つ状態機械を表現するオブジェクトを結合して、新しい状態機械を作ること」*1という解釈でいたのであのように書いていたのですが、CoR は定義に沿って言うと、もっとそれを特化したパターンであるということです。

  • CoR は次のオブジェクトに「責任転嫁」しないといけないので、次のオブジェクトにメッセージを送る前にそれを「加工」するなんて言語道断である。(←ここが勘違いしていたポイント)
  • CoR は、自分がメッセージを処理して応答すべきか、次のオブジェクトに渡すべきか、というプレディケイトに基づいている。

つまりコメント欄のご指摘にあるとおり、「パイプラインパターン」は CoR を包含するより大きな概念なので、「Chain-of-Responsibility をパイプラインパターンで実現することもできそうですね。」といういい方は適切です。

»懺悔ここまで«

で、なぜパイプラインがまずいと言ったかというと、パイプラインという言葉はより実装のさまを想起させてふさわしい言葉に見える一方で、オブジェクト指向プログラミングとは違うパラダイムでも、さまざまな箇所で、様々な層でパイプラインという言葉が使われており (例: CPU)、誤解を招くからです。

なので、パイプラインドイテレータと呼んでみよう。これなら名前空間を汚染することもあるまい。

  • パイプラインドイテレータは、Iterator パターンと Decorator パターン*2の組み合わせです。
  • コルーチンを使うと簡潔にパイプラインドイテレータの制御フローを表現できることがあります。C# では yield 句というシンタクティック・シュガーがあるので、これを利用して書くことができます。

どうでしょうか、こういう解釈で。

ちなみに「IEnumerator をパラメータとして受け取って連鎖を作るなら上の定義に当てはまるかもしれないけど、IEnumerable な場合って、パターンとしてちがくね?」というご指摘があると覚悟していますが、この問いに対する答えとして鍵となるのは次のポイントなのではないでしょうか。

  • IEnumerator のファクトリメソッドである IEnumerable::GetEnumerator() を呼び出したときの IEnumerable の状態がどこまで確定的か。
  • IEnumerable::GetEnumerator() を呼び出した後に IEnumerable の状態が変化するかどうか。

追加で、遅延リストに関して、遅延リストを遅延評価の文脈で解釈すると、遅延リストのイテレータは「開始状態と終了状態が、操作を開始するまで分からないようなイテレータ」という言い方ができると思います。でも、それ以上のものなのかはちょっと分かりません。

*1:電気工学でいう変調器と同じ

*2:Decorator はむしろクラスの「構造的」な側面に着目していて、オブジェクトの「振舞い」とは違った視点で見ているような気がするんですよね。とすると適した落としどころは結局見つからず。こんな基本的な技法すら分類できないなんて、結局 GoF の分類、だめじゃね?と言ってみる。ただ、一番近いの DecoratorなのでDecorator と言う。

C#の好きでない命名規則

GetEnumerator()の話が出てきたついでに、C#の好きでない命名規則について書いておこうと思います。

これもすごく好き嫌いの問題とは思いますが、ファクトリクラスにおいて、Java などである

  • 常に同じインスタンスを返すようなパターンでは getInstance() (専用ファクトリ*1の場合) あるいは getXXX() (複数の種類のクラスを取り扱うファクトリの場合) というメソッド名にする。
  • 呼び出しの度に新しいインスタンスを生成して返すようなパターンでは createInstance() あるいは createXXX() というメソッド名にする。

という暗黙の約束を気に入っていたのですが*2C# では GetXXX() という名称が好まれているようです。

C# は getter があるので C# の中だけでは混乱することはないのかもしれませんが、これは言語というよりセマンティクスに拠る命名規則なので、踏襲してほしかったなと思う次第。

*1:単一の型のオブジェクトしか生成しないファクトリという意味

*2:Java Standard API では、残念ながらこれらの統一がなされていません。ルールに合致しない例: javax.crypto.Cipherなど