C#でインターフェイスを使用しない方法

アプリケーションを設計するときは、多くの場合、インターフェイスと抽象クラスを使用する必要があります。この記事では、「インターフェースの乱用」の一般的な例と、それらを回避するために使用できる戦略について説明します。また、「実装ではなくインターフェイスへのプログラム」という信条の意味についても説明します。

インターフェイスとは何ですか?

まず、インターフェイスと、プログラミングでインターフェイスが必要な理由を理解しましょう。インターフェイスは厳密には契約です。実装はありません。インターフェイスには、メンバー宣言のみが含まれます。メソッド宣言はできますが、定義はできません。インターフェイスで宣言されたメンバーは、インターフェイスを拡張または実装するタイプ(クラスおよび構造体)で実装する必要があります。インターフェイスにフィールドを含めることはできません。インターフェイスはデータメンバーを持つことができないため、シリアル化できません。私が言ったように、インターフェースは宣言のみを持つことができ、定義を持つことはできません。  

インターフェイスに変更を加えないでください 

インターフェイスを拡張するクラスまたは構造体は、そのすべてのメンバーを実装する必要があります。実装が変更されても、コードは引き続き機能します。ただし、コントラクト、つまりインターフェイスが変更された場合は、インターフェイスを拡張するすべてのタイプの実装を変更する必要があります。つまり、インターフェイスを変更すると、インターフェイスを拡張するすべてのタイプに影響します。インターフェイスを拡張するタイプは、コントラクトに準拠する必要があります。したがって、インターフェイスを変更する必要がほとんどない場合にのみ、インターフェイスを使用してください。また、一般的には、既存のインターフェイスを変更するよりも、新しいインターフェイスを作成する方が適切です。 

実装ではなく、インターフェースへのプログラム

「実装ではなくインターフェースにプログラムする」という言葉を時々聞いたことがあるかもしれません。コードでインターフェイスを使用していた可能性がありますが、それでも実装のプログラミングを行っていました。次に、2つのアプローチの違いを調べてみましょう。

インターフェイスにプログラミングするときは、具体的な実装ではなく、最も一般的な抽象化(インターフェイスまたは抽象クラス)を使用します。インターフェイスは均一性を保証するため、インターフェイスへのプログラミングは、同様のオブジェクトを均一に処理できることを意味します。そうすることで、実装か​​ら切り離されます。つまり、実装は変わる可能性があります。これにより、デザインにも柔軟性が加わります。

次のコードスニペットは、インターフェイスへのプログラミングを示しています。いくつかのメソッドの宣言を含むIRepositoryという名前のインターフェースについて考えてみます。以下に示すように、ProductRepositoryクラスとCustomerRepositoryクラスは、IRepositoryインターフェイスを拡張し、IRepositoryインターフェイスで宣言されたメソッドを実装します。

パブリックインターフェイスIRepository

    {{

        //いくつかのコード

    }

    パブリッククラスProductRepository:IRepository

    {{

        //いくつかのコード

    }

    パブリッククラスCustomerRepository:IRepository

    {{

        //いくつかのコード

    }

次のコードを使用して、ProductRepositoryのインスタンスを作成できます。

IRepositoryリポジトリ= new ProductRepository();

ここでは、IRepositoryインターフェイスを実装する任意のクラスを使用できるという考え方です。したがって、次のステートメントも有効です。

IRepositoryリポジトリ= new CustomerRepository();

実装にプログラムすると、この均一性は失われます。代わりに、通常、コードの動作を制御するために、「if..else」や「switch..case」ステートメントなどのいくつかの構造があります。

インターフェースの乱用を避ける

すべてのクラスをインターフェースに関連付けることは良い習慣ではありません。このようにインターフェースを使いすぎると、不必要な複雑さが生じ、コードの冗長性が生じ、YAGNIに違反し、コードベースの可読性と保守性が低下します。インターフェイスは、同じ動作をするオブジェクトをグループ化するために使用されます。オブジェクトの動作が同じでない場合、このグループ化は必要ありません。複数の実装を行わないときにインターフェースを使用することは、インターフェースの乱用の例です。

クラスのパブリックメンバーと一致するクラスのインターフェイスを作成することは非常に一般的です。そうすることで、値をまったく追加しません。実際の抽象化を追加せずに、クラスのインターフェースを複製するだけです。

次に、インターフェースがどのように過剰に使用されているかの例を見てみましょう。IProductという名前の次のインターフェイスについて考えてみます。

パブリックインターフェイスIProduct

    {{

        int Id {get; セットする; }

        string ProductName {get; セットする; }

        ダブル価格{取得; セットする; }

        int数量{取得; セットする; }

    }

Productクラスは、以下に示すようにIProductインターフェイスを拡張します。

パブリッククラス製品:IProduct

    {{

        public int Id {get; セットする; }

        パブリック文字列ProductName {get; セットする; }

        パブリックダブル価格{取得; セットする; }

        publicint数量{取得; セットする; }

    }

明らかに、インターフェイスとその実装は同じであるため、IProductインターフェイスは必要ありません。冗長なコードは不要です。

別の例を見てみましょう。次のコードスニペットは、SaveとUpdateの2つのメソッドを宣言するIProductManagerという名前のインターフェイスを示しています。

 パブリックインターフェイスIProductManager

    {{

        void Save(IProduct product);

        void Update(IProduct product);

    }

IProductManagerインターフェイスには、ProductManagerクラスのパブリックメソッドの宣言が含まれています。ProductManagerクラスは次のようになります。

 パブリッククラスProductManager:IProductManager

    {{

        public void Save(IProduct product)

        {{

           //ここに実装を記述します

        }

        public void Update(IProduct product)

        {{

            //ここに実装を記述します

        }

    }

IProductおよびIProductManagerインターフェースは、インターフェースの乱用の例です。これらのインターフェースは両方とも単一の実装を持ち、まったく価値を追加しません。

インターフェイスを使用することで、コード内の不要な結合を削除し、コードを簡単にテストできるようにすることができます。ただし、インターフェイスの使いすぎは避けてください。インターフェイスは、複数の実装がある場合にのみ使用してください。果たすべき役割が多いクラスや複数の責任を持つクラスがある場合にも、インターフェースを使用できます。この場合、クラスは複数のインターフェースを実装できます—役割ごとに1つ。