interfaceは要るのか

 クロージャを一々渡してられないようなものもある。GetEnumerator, Count, インデクサ、Dispose辺りはいちいち自分で「Disposeのメソッドはこれです」なんて指定していたら発狂してしまうかもしれない。そういうものにはinterfaceは必要だろう。あとフレームワークはコレクションのAddを欲しがる。

IEnumerable<T> GetEnumerator()
ICollection<T> Count{ get; }
IList<T> this[int index]{ get; }
IDictionary<T> TryGetValue(key, out value) 要るか?
IDisposable Dispose()
ICanAdd<T> Add(T item)
IComparable<T> CompareTo(T item)

 こんなもんだろうか。最初からC# 3.0の言語機能があるなら、俺だったらこれ以外のinterfaceは作らなかった。いや、どうだろう。IEquatableっているのかな。パフォーマンスのためにあった方がいいのか。 IEqualityComparerは後から入れるやつだから、後から入れるのはクロージャの方がいい。EqualityComparer.Defaultはobjectをkeyにしたい場合は必要だけど、その値がIEqualityComparer<T>である必要は無い。単一の目的を持ったクラスを作るだけなんだから抽象クラスでもいいし、それよりクロージャの方がいい。

 Addなんかはなしにして配列をとるコンストラクタとかIEnumerableをとるコンストラクタとかを呼び出すなり受け取るなりすればいいような気もするが。ReadOnlyListに対する扱いがぞんざいすぎる気がする。

 interfaceにした方がいい条件というのは

デフォルト動作を指定する確率が高いので指定させる必要が無い &&
頻繁に出てくるので指定させるとうるさくなる &&
オブジェクトの基本的な属性なので他の属性と重なったオブジェクトが作れないと困る

 かなり絞られてくると思う。自作する必要なんてまずないだろう。ユーザーが動作を指定できるようにするにはクロージャの方がよほどいいし、動作を指定する必要がほとんどなくてもオブジェクトの基本的な属性でないなら継承でいい。

 それを見越していたのか、Windows.Formsはびっくりするほどinterfaceがない。代わりに関数型の変数が腐るほどある。インターフェースを実装したクラスを作らせるなんてのは本当に最悪のコードを苦痛を伴って書かせるだけの行為だということを良く分かっている。自分でinterfaceを実装しなさいなんていうコードは今後二度と書いちゃいけない。

 interfaceを使ってもいいケースは、デフォルト実装を使うのが当たり前すぎてデフォルト実装を指定する部分がわずらわしく感じるが、オブジェクトの基本的な属性なので継承できないことがある場合、単一目的のオブジェクトに出来ないことがある場合だけだ。つまりそんな場合はまずないということだ。まずないというか無いようにしなきゃいけない。interfaceはデフォルト実装を指定するためにばかり使われるので、ダラダラと委譲コードを書き連ねるハメになる。間にクラスが挟まるので見通しが悪くなり、めんどくさいし何にもいいことはない。

 IListなんか馬鹿みたいにメンバが一杯あるから、実装するのも大変だし、C#4.0の共変反変にも対応できていない。また、インデックスを指定するアルゴリズムを書いたらLinkedListで大変なことになるなど難しい問題が目白押しだ。しかしIListしか引数として取れない多くのメソッドがあるから、LinkedListにもIListを実装したいという需要は出てくる。しかし結局.NET FrameworkではLinkedListはIListを実装していない。

 そうじゃなくて、たとえばその処理に末尾のAddとRemoveが必要なら、

void Process<T>(Action<T> push, Action pop)

List<T> list = new List<T>();
Process(list.Add, () => list.RemoveAt(list.Count - 1))

 こうすればいい。めんどくさいなら、自分で

void Process<T>(List<T> list)
{
  Process(list.Add, () => list.RemoveAt(list.Count - 1);
}

 こういうのを作ればいい。これさえあれば

LinkedList<T> linkedList = new LinkedList<T>();
Process(linkedList.AddLast, linkedList.RemoveLast)

 LinkedListにもちゃんと適用できる。

 でもなんかもうちょっとあるような気もするなあ。interfaceにした方がいい場合が。