7

背景: 「実装ではなくインターフェイスへのプログラム」Haskell 型クラスの精神に則り、コーディングの実験として、主にインターフェイスと拡張機能の組み合わせに基づいた API を作成するとはどういうことかを考えています。メソッド。私は次の 2 つのガイドラインを念頭に置いています。

  1. クラスの継承は可能な限り避けてください。インターフェイスはsealed classes として実装する必要があります。
    (これには 2 つの理由があります。1 つ目は、サブクラス化によって、派生クラスで基本クラスのコントラクトを指定して強制する方法について厄介な問題が発生するためです。2 つ目は、Haskell 型クラスの影響であり、ポリモーフィズムはサブクラス化を必要としません。)

  2. 可能な限りインスタンス メソッドは避けてください。拡張メソッドで実行できる場合は、これらが優先されます。
    (これは、インターフェイスをコンパクトに保つ​​のに役立つことを目的としています。他のインスタンス メソッドの組み合わせによって実行できることはすべて、拡張メソッドになります。インターフェイスに残っているのは、コア機能と、特に状態変更メソッドです。)

問題: 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) に対する十分に議論された改善を歓迎します。

4

2 に答える 2

6

あなたのガイドラインはそのままで十分です。すでに「可能な限り」と言っています。したがって、実際のタスクは、「可能な限り」ビットをさらに詳細に説明することです。

私は次の単純な二分法を使用します。メソッドを追加する目的がサブクラス間の違いを隠すことである場合は、拡張メソッドを使用します。違いを強調することが目的の場合は、仮想メソッドを使用します。

あなたのEatメソッドは、サブクラス間の違いを導入するメソッドの例です。リンゴを食べる (または食べない) プロセスは、リンゴの種類によって異なります。したがって、インスタンス メソッドとして実装する必要があります。

違いを隠そうとするメソッドの例は次のThrowAwayとおりです。

public static void ThrowAway(this IApple apple) {
    var theBin = RecycleBins.FindCompostBin();
    if (theBin != null) {
        theBin.Accept(apple);
        return;
    }
    apple.CutUp();
    RecycleBins.FindGarbage().Accept(apple);
}

リンゴの種類に関係なく、リンゴを捨てるプロセスが同じである場合、その操作は拡張メソッドで実装する最有力候補です。

于 2012-07-11T22:00:19.863 に答える
1

私にとって、期待される出力は正しかった。変数を IApple として型キャストしました (おそらく間違った使い方をしています)。

例えば:

IApple apple = new RottenApple();
apple.Eat();  // "Yummy, that was good!"
IRottenApple apple2 = new RottenApple();
apple2.Eat(); // "Eat it yourself, you disgusting human, you!"
var apple3 = new RottenApple();
apple.Eat();  // "Eat it yourself, you disgusting human, you!"

質問: 拡張メソッドと (仮想) インスタンス メソッドの使用について、洗練された/より正確なガイドラインは何ですか? 「インターフェースへのプログラミング」のための拡張メソッドの使用は、いつ行われますか? インスタンスメソッドが実際に必要になるのはどのような場合ですか?

アプリケーションを開発するときの私の個人的な意見:

私は、自分または他の誰かが消費する可能性のあるものを作成するときに、インスタンス メソッドを使用します。これは、型が実際に何であるかの要件であるためです。FlyingObjectmethod を持つインターフェイス/クラスを考えてみましょうFly()。それが飛翔体の基本的な基本的な方法です。拡張メソッドを作成しても意味がありません。

私は(多くの)Extension メソッドを使用していますが、これらは拡張するクラスの使用の要件ではありません。たとえば、 (さらに内部で)intを作成する拡張メソッドがあります。SqlParameterそれでも、そのメソッドを int の基本クラスの一部として持つことは意味がありません。実際には、int が何であるか、または行うこととは何の関係もありません。拡張メソッドは、クラス/構造体を使用する再利用可能なメソッドを作成する視覚的に優れた方法です。

于 2012-07-11T22:09:06.123 に答える