2

DI とベスト プラクティスについて読んでいますが、この質問に対する答えはまだ見つかりません。いつインターフェイスを使用する必要がありますか?

  1. 一部の開発者は、注入されるすべてのオブジェクトにインターフェースを追加することを提案しています。これにより、モジュラー アプリケーションが作成されます。
  2. これに反対する人もいます。

だから私の質問はどれが正しいですか?

編集:

以下に 2 つの側面を示しますが、インターフェイスを使用する利点はまだわかりません。どちらの場合でも、クラスを簡単にモックして、実装を変更できます

インターフェイスの使用

bind(IUserStorage.class).to(UserStorage.class);
// Unit test
bind(IUserStorage.class).to(Mock(UserStorage.class));

インターフェイスを使用しない

bind(UserStorage.class).to(UserStorage.class);
// Unit test
bind(UserStorage.class).to(Mock(UserStorage.class));
4

4 に答える 4

4

インターフェイスを使用することが OOP の原則に反しているとは信じられません。

このシナリオでは間違いなくインターフェイスを使用します。これは、コンポーネントを疎結合していることを意味し、簡単にモックしたり、代替案を代用したりできます。多くの DI フレームワークは、追加機能を提供するためにインターフェイスを使用します (たとえば、実際のオブジェクトにマップされたプロキシ オブジェクトを作成しますが、追加機能を備えています)。

そのため、最も些細な注入オブジェクトを除いて、すべてのインターフェイスを試して使用します。ある段階で、代替可能性、フレームワーク コード生成などを利用したいと思うでしょう。また、インターフェイスの使用を後付けすることは、プロジェクトの開始時に簡単に回避できる追加の苦痛です。

于 2012-12-17T17:36:53.300 に答える
3

インターフェイス ベースの設計は IoC の基礎です。ここでインターフェイス ベースの設計について簡単に説明します (自分のブログを参照していて申し訳ありませんが、これに関する記事を書き終えたところです。これは私の修士論文からの抜粋でした)。

ナンディガム等。インターフェースベースの設計を、「インターフェースを使用した設計の利点を享受するために、設計の可能な限り、意識的かつ積極的にインターフェースを定義して使用する、オブジェクト指向システムを開発する方法」と定義しています [Nan09]。インターフェイスベースの設計のアプリケーションは、「実装ではなく、インターフェイスへのプログラム」という原則に従います。この原則は、結果として得られるシステム [Dav03] に次の利点をもたらします:柔軟性(変更に対するシステムの堅牢性を説明する)、拡張性(システムが追加に対応できる容易さ)、およびプラグ可能性 (同一のインターフェースを持つオブジェクトの置き換えを可能にする能力)ランタイム)。

インターフェイス設計と IC を組み合わせると次の利点が得られます。

  1. タスクは実装から分離されています。
  2. モジュールがコントラクト (インターフェイス) だけで他のモジュールに依存するモジュールを高めます。
  3. プラグ可能性が向上し、モジュールを交換しても他のモジュールにカスケード効果がありません。

あなたの質問に答えるために、私はさまざまなタイプのモジュールにインターフェースを使用します。たとえば、サービスまたはリポジトリごとに 1 つです。

コントローラーやモデル クラス (MVC アプリ) のインターフェイスは作成しません。

これらすべての副作用として、テストが容易になります。

于 2012-12-17T17:50:37.320 に答える
2

インターフェイスまたは少なくとも抽象/継承可能なクラスを使用する場合、DI/IoC 構成で実装を簡単に交換 (別のクラスを挿入) することで、プログラムの動作を変更できます。 インターフェースを使用することは良い習慣です(imho)。これは、モックを必要とする UnitTests を作成している場合に特に重要です。インターフェースを使用していない場合、適切なカバレッジで UnitTests を作成することは非常に困難です (ほとんどの「現実世界の」ケースでは不可能とは言えません)。

注入部分が変化する可能性がある場合は、インターフェイスを使用する必要があると思います。実装を拡張するのは簡単なはずです。 Open-Closed-Principleを参照してください。=> これには、モジュール/パーツ/実装の交換が必要になります...クラスにオーバーライドする仮想関数がなく、実装を変更せざるを得ない場合にどうなるか自問してください。

少なくともパブリッククラス/コードの一部(他のプログラマーが使用する部分)にはインターフェイスを使用します。

あなたのサンプルを見てください。問題はワイヤリング部分にあり、インターフェースの (デフォルト) 実装としてのクラスのバインディングだけではありません (バインディングは機能しますが、ワイヤリングが壊れる可能性があります)。

たとえば、2 つの実装がある場合 (ここの C# サンプルは、Java などでも同じである必要があります):

public interface IUserStorage
{
  void Write(object something);
}

public class UserStorageTextFile : IUserStorage
{
   public void Write(object something) { ... }; // stores to text file
}

public class UserStorageDB : IUserStorage
{
   public void Write(object something) { ... }; // stores to DB
}

public class MyStorageClient
{
   public MyStorageClient(IUserStorage storage) { ... } // copy to private field and use it etc.
}

IoC によっては、MyStorageClient のインスタンスを IUserStorage のバインディングに簡単に接続できるはずです。

bind(IUserStorage.class).to(UserStorageDB.class); // Java sample, eh?

しかし、MyStorageClient がすでに DB を使用することを強く強制されている場合...

public class MyStorageClient
{
   public MyStorageClient(UserStorageDB storage) { ... } // copy to private field and use it etc.
}

... UserStorageTextFile が UserStorageDB から継承されていることを除いて、UserStorageTextFile クラスと結び付けることは不可能です...しかし、単純なテキストファイルのみを書きたい場合、たとえば Oracle ドライバー (UserStorageDB によって必要とされる) に依存する必要があるのはなぜですか? ?

サンプルは十分に明確で、インターフェースを使用する利点を示していると思います...

そうでない場合は...これを試してください:

bind(UserStorageDB.class).to(UserStorageTextFile.class);

// and in another config/module/unitTest
bind(UserStorageTextFile.class).to(Mock(UserStorageDB.class));

// and try to wire it against your client class, too (both ways, meaning one config for TextFile and load a config for the DB after changing only the configuration)
于 2012-12-17T17:42:21.897 に答える
-1

あなたの質問は「一部の開発者[これに賛成]」と「一部の開発者[これに反対]」と述べているので、正しい答えはありません。しかし、これが私がインターフェースが乱用されていることに同意する理由です

ライブラリを作成する場合は、インターフェイスをいつ使用するかを選択することが重要です。コードの消費方法を制御しないと、保守可能なコントラクトを作成するのが難しくなります。

ただし、アプリケーションを作成している場合は、クラスのパブリックインターフェイスがコードを消費するための保守可能なコントラクトとして機能できるため、インターフェイスが必要になる可能性は低くなります。バージョン1が次のようになっているとしましょう。

public class UserStorage
{
    public void Store(User user) { /* ... */ }
}

これに変更するためにリファクタリングツールも必要ありません。

public interface UserStorage
{
    public void Store(User user);
}

class TheImplementation implements IUserStorage
{
    public void Store(User user) { /* ... */ }
}

次に、リファクタリングツールを使用して、インターフェイスの名前をに簡単に変更できますIUserStorage

したがって、ライブラリ以外のコードを記述している場合は、通常、スワップ可能な実装やデコレータなどが必要になるまでクラスを使用できません。クラスのパブリックインターフェイスがニーズに合わない場合は、インターフェイスを使用する必要があります。(たとえば、インターフェイス分離の原則を参照してください)

要するに、クラスと1:1のインターフェースを持つことは、アプリケーションコードで不必要な間接参照です。

于 2012-12-17T19:28:42.903 に答える