背景: 「実装ではなくインターフェイスへのプログラム」とHaskell 型クラスの精神に則り、コーディングの実験として、主にインターフェイスと拡張機能の組み合わせに基づいた API を作成するとはどういうことかを考えています。メソッド。私は次の 2 つのガイドラインを念頭に置いています。
クラスの継承は可能な限り避けてください。インターフェイスは
sealed class
es として実装する必要があります。
(これには 2 つの理由があります。1 つ目は、サブクラス化によって、派生クラスで基本クラスのコントラクトを指定して強制する方法について厄介な問題が発生するためです。2 つ目は、Haskell 型クラスの影響であり、ポリモーフィズムはサブクラス化を必要としません。)可能な限りインスタンス メソッドは避けてください。拡張メソッドで実行できる場合は、これらが優先されます。
(これは、インターフェイスをコンパクトに保つのに役立つことを目的としています。他のインスタンス メソッドの組み合わせによって実行できることはすべて、拡張メソッドになります。インターフェイスに残っているのは、コア機能と、特に状態変更メソッドです。)
問題: 2 番目のガイドラインに問題があります。このことを考慮:
interface IApple { }
static void Eat(this IApple apple)
{
Console.WriteLine("Yummy, that was good!");
}
interface IRottenApple : IApple { }
static void Eat(this IRottenApple apple)
{
Console.WriteLine("Eat it yourself, you disgusting human, you!");
}
sealed class RottenApple : IRottenApple { }
IApple apple = new RottenApple();
// API user might expect virtual dispatch to happen (as usual) when 'Eat' is called:
apple.Eat(); // ==> "Yummy, that was good!"
明らかに、期待される結果 ( "Eat it yourself…"
) についてEat
は、通常のインスタンス メソッドである必要があります。
質問:拡張メソッドと (仮想) インスタンス メソッドの使用について、洗練された/より正確なガイドラインは何ですか? 「インターフェースへのプログラミング」のための拡張メソッドの使用が行き過ぎになるのはいつですか? インスタンスメソッドが実際に必要になるのはどのような場合ですか?
明確で一般的なルールがあるかどうかはわかりませんので、完璧で普遍的な答えは期待していません。上記のガイドライン (2) に対する十分に議論された改善を歓迎します。