web-dev-qa-db-ja.com

なぜインターフェースを使用したいのですか?

メソッドなどを実装することを強制することを理解していますが、理解できないのは、なぜそれらを使用するのかということです。なぜ私がこれを実装したいのかについての良い例や説明を誰かに教えてもらえますか?.

69
user23661

1つの具体例:インターフェイスは、他の人のコードが満たす必要のあるコントラクトを指定するための良い方法です。

コードのライブラリを作成している場合は、特定の動作セットを持つオブジェクトに有効なコードを作成できます。最善の解決策は、これらの動作をインターフェイス(実装ではなく、説明のみ)で指定し、ライブラリインターフェイスでそのインターフェイスを実装するオブジェクトへの参照を使用することです。

その後、任意のランダムな人物がやって来て、そのインターフェースを実装するクラスを作成し、そのクラスのオブジェクトをインスタンス化して、それをライブラリー・コードに渡して、機能することを期待できます。 注:もちろん、インターフェースの意図を無視して厳密にインターフェースを実装することは可能です。そのため、インターフェースを実装するだけでは、動作が保証されません。バカはいつも道を見つける! :-)

別の具体例:協力しなければならないさまざまなコンポーネントに取り組んでいる2つのチーム。 2つのチームが1日目に着席し、一連のインターフェースについて合意した場合、2つのチームは別々の方法でそれらのインターフェースを中心にコンポーネントを実装できます。チームAは、テストのためにチームBからのコンポーネントをシミュレートするテストハーネスを構築できます。並行開発、バグの減少。

重要な点は、インターフェイスがabstractionのレイヤーを提供するため、不要な詳細を無視したコードを作成できることです。

正規の例ほとんどの教科書で使用されているのは、ソートルーチンの例です。 2つのオブジェクトを比較する方法がある限り、任意のクラスのオブジェクトを並べ替えることができます。したがって、IComparableインターフェイスを実装することで、任意のクラスをソート可能にすることができます。これにより、2つのインスタンスを比較するメソッドを実装する必要があります。すべての並べ替えルーチンは、IComparableオブジェクトへの参照を処理するように記述されているため、IComparableを実装するとすぐに、クラスのオブジェクトのコレクションでこれらの並べ替えルーチンを使用できます。

61
Stewart Johnson

インターフェイスはcontractsを定義し、それがキーワードです。

プログラムでコントラクトを定義する必要があるが、コントラクトを実行する限り、そのコントラクトを満たすクラスの残りのプロパティについては特に気にしないときにインターフェイスを使用します。

それでは、例を見てみましょう。リストをソートする機能を提供するメソッドがあるとします。まず、リストとは何ですか?リストをソートするために、どの要素が保持されているか本当に気にしますか?あなたの答えはノーでなければなりません... .NET(たとえば)には、リストがサポートしなければならない操作を定義するIListと呼ばれるインターフェイスがあり、表面下の実際の詳細を気にしません。

例に戻ると、リスト内のオブジェクトのクラスは本当にわかりません...気にしません。オブジェクトを比較するだけの場合は、並べ替えるのもよいでしょう。だからあなたは契約を宣言します:

interface IComparable
{
  // Return -1 if this is less than CompareWith
  // Return 0 if object are equal
  // Return 1 if CompareWith is less than this
  int Compare(object CompareWith);
}

そのコントラクトは、比較可能にするために、オブジェクトを受け入れてintを返すメソッドを実装する必要があることを指定しています。これでコントラクトが定義されました。今のところ、オブジェクト自体は問題ではありませんが、contractが問題なので、次のようにします。

IComparable comp1 = list.GetItem(i) as IComparable;

if (comp1.Compare(list.GetItem(i+1)) < 0)
  swapItem(list,i, i+1)

PS:私は例が少し素朴であることを知っていますが、それらは例です...

10
Jorge Córdoba

典型的な例の1つはプラグインアーキテクチャです。開発者Aがメインアプリを作成し、開発者B、C、Dによって作成されたすべてのプラグインが、アプリが期待するものに準拠していることを確認します。

9
Treb

インターフェイスを理解する最も簡単な方法は、さまざまなオブジェクトが共通の機能を公開できるようにすることです。これにより、プログラマーは、オブジェクトがそのインターフェースを実装する限り、インターフェースにプログラムする、はるかに単純で短いコードを書くことができます。

例1: MySQL、MSSQL、Oracleなど、さまざまなデータベースプロバイダーがあります。ただし、すべてのデータベースオブジェクトが同じことを実行できるため、データベースオブジェクトの多くのインターフェイスが見つかります。オブジェクトがIDBConnectionを実装している場合、オブジェクトはメソッドOpen()およびClose()を公開します。したがって、自分のプログラムをデータベースプロバイダーにとらわれないようにするには、特定のプロバイダーではなく、インターフェイスにプログラムします。

IDbConnection connection = GetDatabaseConnectionFromConfig()
connection.Open()
// do stuff
connection.Close()

インターフェイス(IDbconnection)へのプログラミングを参照してください。構成内の任意のデータプロバイダーをスワップアウトできますが、コードはまったく同じです。この柔軟性は非常に便利で、保守が容易です。これの欠点は、「一般的な」データベース操作しか実行できず、特定の各プロバイダーが提供する長所を十分に活用できない可能性があるため、プログラミングのすべてにおいてトレードオフがあり、どのシナリオが最もメリットがあるかを判断する必要があることです。

例2:ほとんどすべてのコレクションがIEnumerableと呼ばれるこのインターフェイスを実装していることに気付いた場合。 IEnumerableは、MoveNext()、Current、およびReset()を持つIEnumeratorを返します。これにより、C#はコレクション内を簡単に移動できます。これができる理由は、オブジェクトがそれを通過するために必要なメソッドをオブジェクトが公開していることを知っているIEnumerableインターフェイスを公開しているからです。これは2つのことを行います。 1)foreachループがコレクションを列挙する方法を認識し、2)強力なLINQ式をコレクションに適用できるようになりました。ここでもインターフェイスが非常に役立つ理由は、すべてのコレクションにCOMMONに何かがあるため、移動できるためです。各コレクションは別の方法(リンクされたリストと配列)で移動できますが、インターフェイスの優れている点は、実装が非表示であり、インターフェイスのコンシューマーとは無関係であることです。 MoveNext()は、コレクション内の次のアイテムを提供します。どのように実行するかは関係ありません。かなりいいですね

例3:独自のインターフェースを設計する場合は、1つ質問するだけで済みます。これらの共通点は何ですか?オブジェクトが共有するすべてのものを見つけたら、それらのプロパティ/メソッドをインターフェイスに抽象化して、各オブジェクトがインターフェイスから継承できるようにします。その後、1つのインターフェースを使用して、複数のオブジェクトに対してプログラミングできます。

そしてもちろん、私は私のお気に入りのC++多型の例である動物の例を挙げなければなりません。すべての動物は特定の特徴を共有しています。彼らが移動、話すことができ、すべてに名前があるとしましょう。私はすべての動物が共通して持っているものを識別し、それらの性質をIAnimalインターフェースに抽象化できるので。次に、このインターフェイスを実装するBearオブジェクト、Owlオブジェクト、およびSnakeオブジェクトを作成します。同じインターフェイスを実装する異なるオブジェクトを一緒に格納できるのは、インターフェイスがIS-Aレプリケーションを表すためです。クマはIS-A動物、フクロウはIS-A動物なので、すべてを動物として集めることができます。

var animals = new IAnimal[] = {new Bear(), new Owl(), new Snake()} // here I can collect different objects in a single collection because they inherit from the same interface

foreach (IAnimal animal in animals) 
{
    Console.WriteLine(animal.Name)
    animal.Speak() // a bear growls, a owl hoots, and a snake hisses
    animal.Move() // bear runs, owl flys, snake slithers
}

これらの動物はそれぞれのアクションを異なる方法で実行しますが、1つの統合モデルでそれらに対してすべてプログラムできることがわかります。これは、インターフェースの多くの利点の1つにすぎません。

したがって、インターフェイスで最も重要なことは、オブジェクトが共通して何を持っているかということです。これにより、同じ方法でDIFFERENTオブジェクトをプログラミングできます。時間を節約し、より柔軟なアプリケーションを作成し、複雑さ/実装を隠し、現実世界のオブジェクト/状況をモデル化するなど、多くの利点があります。

お役に立てれば。

8
Despertar

車のペダルはインターフェースを実装しています。私は道路の右側を運転しているアメリカ出身です。ステアリングホイールは車の左側にあります。マニュアルトランスミッションのペダルは、左から右にクラッチ->ブレーキ->アクセルです。私がアイルランドに行ったとき、運転は逆になります。車のステアリングホイールは右側にあり、道路の左側を走行します...しかし、ペダル、ああ、ペダル...彼らは同じインターフェースを実装しました... 3つのペダルはすべて同じ順序でした...そのため、クラスが異なっていて、クラスが動作するネットワークが異なっていても、ペダルインターフェースは快適でした。私の脳は、他のすべての車と同じように、この車で私の筋肉を呼ぶことができました。

私たちがなくてはならない、プログラミング以外の多くのインターフェースについて考えてみてください。次に、あなた自身の質問に答えてください。

5
Mark Brady

ポリモーフィズムの活用を期待するオブジェクト指向システムでは、インターフェースが絶対に必要です。

典型的な例は、Move()メソッドを持つIVehicleです。 IVehicleを実装するクラスCar、Bike、Tankを作成できます。それらはすべてMove()を実行できます。また、Move()を実行できるようにするために、どの種類の車両を扱っていても気にしないコードを記述できます。

void MoveAVehicle(IVehicle vehicle)
{
    vehicle.Move();
}
4
Eric Z Beard
When you need different classes to share same methods you use Interfaces.
4
Lev

インターフェイスはポリモーフィズムの一種です。例:

いくつかのロギングコードを記述したいとします。ロギングはどこかに行きます(おそらく、ファイル、またはメインコードが実行されているデバイスのシリアルポート、またはソケット、または/ dev/nullのように破棄されます)。あなたはどこにいるのかわからない:あなたのロギングコードのユーザーはそれを決定するために自由である必要がある。実際、ロギングコードは関係ありません。それは単にバイトを書き込むことができる何かを望んでいます。

したがって、「バイトを書き込むことができるもの」と呼ばれるインターフェイスを発明します。ロギングコードには、このインターフェイスのインスタンスが与えられます(おそらく実行時に、おそらくコンパイル時に構成されます。それでも、ポリモーフィズムであり、種類が異なるだけです)。インターフェースを実装する1つ以上のクラスを記述し、ロギングコードが使用するクラスを変更するだけで、ロギングの場所を簡単に変更できます。他の誰かが、コードを変更せずに、インターフェースの独自の実装を作成することで、ロギングの場所を変更できます。これが基本的にポリモーフィズムの意味です。特定の方法でオブジェクトを使用するのに十分なだけの知識を持ちながら、知る必要のないあらゆる点で変化させることができます。インターフェースは、あなたが知る必要があることを記述します。

Cのファイル記述子は基本的に「バイトの読み書きができるもの」のインターフェースであり、ほとんどすべての型付き言語はその標準ライブラリー(ストリームなど)に潜んでいるようなインターフェースを持っています。型なし言語には、通常、ストリームを表す非公式の型(おそらくコントラクトと呼ばれる)があります。したがって、実際には、この特定のインターフェースを実際に自分で発明する必要はほとんどありません。言語が提供するものを使用します。

ロギングとストリームは一例にすぎません-オブジェクトが何をすることになっているのかを抽象的な言葉で説明できるが、特定の実装/クラス/ものに結び付けたくない場合はいつでもインターフェースが発生します。

3
Steve Jessop

そうする理由はいくつかあります。インターフェイスを使用する場合、コードをリファクタリング/書き直す必要があるときはいつでも準備ができています。単純な操作のために、一種の標準化されたAPIを提供することもできます。

たとえば、クイックソートのようなソートアルゴリズムを作成する場合、オブジェクトのリストをソートする必要があるのは、2つのオブジェクトを正常に比較できることだけです。 ISortableなどのインターフェイスを作成すると、オブジェクトを作成する誰もがISortableインターフェイスを実装でき、ソートコードを使用できます。

データベースストレージを使用するコードを記述していて、ストレージインターフェイスに書き込む場合は、そのコードをすぐに置き換えることができます。

インターフェースは、コードの疎結合を促進するため、柔軟性が向上します。

2
Douglas Mayle

基本的なCRUDメカニズムを定義する次の基本的なインターフェースを想像してください。

interface Storable {
    function create($data);
    function read($id);
    function update($data, $id);
    function delete($id);
}

このインターフェースから、それを実装するすべてのオブジェクトに、データの作成、読み取り、更新、および削除を行う機能が必要であることがわかります。これは、データベース接続、CSVファイルリーダー、XMLファイルリーダー、またはCRUD操作を使用する可能性のあるその他の種類のメカニズムによるものです。

したがって、次のようなものができます。

class Logger {
    Storable storage;

    function Logger(Storable storage) {
        this.storage = storage;
    }

    function writeLogEntry() {
        this.storage.create("I am a log entry");
    }
}

このロガーは、データベース接続、またはディスク上のファイルを操作するものを渡すかどうかを気にしません。知っておく必要があるのは、それに対してcreate()を呼び出すことができ、期待どおりに機能することだけです。

これから発生する次の問題は、データベースやCSVファイルなどがすべてデータを格納できる場合、それらを汎用のStorableオブジェクトから継承して、インターフェイスの必要性をなくすべきではないかということです。これに対する答えはノーです...すべてのデータベース接続がCRUD操作を実装しているとは限りません。同じことがすべてのファイルリーダーに当てはまります。

インターフェースは、オブジェクトが実行できる機能と、オブジェクトの使用方法を定義します。

2
JamShady

お気づきのように、インターフェイスは、特定の形式で作成するように強制したい場合に適しています。

インターフェイスは、特定の形式でないデータがコードで危険な仮定を行うことを意味する場合に適しています。

たとえば、現時点では、ある形式から別の形式にデータを変換するアプリケーションを作成しています。私はそれらにそれらのフィールドを配置することを強制したいので、私はknowそれらが存在し、適切に実装される可能性が高くなります。とにかくデータが必要になる可能性が高いため、別のバージョンが出てもコンパイルされないかどうかは気にしません。

これが原因でインターフェースが使用されることはめったにありません。通常、仮定を立てることができるか、実際にはrequireデータを必要としないためです。

1
Kenny Mann

インターフェースは、単にinterfaceを定義します。後で、インターフェースをパラメーター(より正確には、そのインターフェースを実装するオブジェクト)として受け入れた(他のクラスの)メソッドを定義できます。このようにして、メソッドは多種多様なオブジェクトを操作できます。その唯一の共通点は、それらがそのインターフェースを実装することです。

1
James Curran

最初に、それらは抽象化の追加レイヤーを提供します。と言うことができます。 」そして、おそらくこれらのメソッドの意味を、何らかの形で抽象化された用語で設定し、コードについて推論することもできます。 duck-typed言語では、無料で入手できます。明示的な構文「インターフェース」は必要ありません。それでも、おそらくあなたはまだ概念的なインターフェースのセット契約のようなもの(Design by Contractなど)を作成しています。

さらに、インターフェースは「純粋な」目的で使用されないこともあります。 Javaでは、これらを使用して多重継承をエミュレートできます。 C++では、これらを使用してコンパイル時間を短縮できます。

一般に、これらはコード内の結合を減らします。それは良いことです。

この方法を使用すると、コードのテストも簡単になります。

1
Paweł Hajdan

もののコレクションを追跡したいとします。これらのコレクションは、アイテムの追加や削除、アイテムがコレクションに含まれているかどうかのチェックなど、多くのことをサポートする必要があります。

次に、メソッドadd()、remove()、contains()でインターフェイスICollectionを指定できます。

どの種類のコレクション(リスト、配列、ハッシュテーブル、赤黒木など)を認識する必要がないコードは、インターフェイスを実装したオブジェクトを受け入れ、実際の型を知らなくてもそれらを操作できます。

1
jakber

.Netでは、基本クラスを作成し、クラスが何らかの形で関連している場合はそれらから継承します。たとえば、基本クラスのPersonは、EmployeeとCustomerに継承できます。人は、住所フィールド、名前、電話などの共通のプロパティを持つ場合があります。従業員は独自の部門プロパティを持っている可能性があります。顧客は他の独占的な特性を持っています。

クラスは.Net内の他の1つのクラスからのみ継承できるため、追加の共有機能にはインターフェイスを使用します。インターフェースは、他の点では無関係なクラスによって共有される場合があります。インターフェースを使用すると、開発者がそれを実装する他のすべてのクラスで共有されていることがわかるコントラクトが作成されます。また、これらのクラスにそのすべてのメンバーを実装するように強制します。

1
DOK

私のブログの記事で、インターフェースが持つ3つの目的について簡単に説明します。

インターフェースにはさまざまな目的があります。

  • 同じ目標に対して異なる実装を提供します。典型的な例はリストであり、パフォーマンスのユースケース(LinkedList、ArrayListなど)によって実装が異なる場合があります。
  • 基準の変更を許可します。たとえば、並べ替え関数は、同じアルゴリズムに基づいて、あらゆる種類の並べ替え基準を提供するためにComparableインターフェイスを受け入れる場合があります。
  • 実装の詳細を非表示にします。また、インターフェースの本体にはメソッド、フィールド、コメントのみがあり、スキップするコードの長いチャンクはないため、ユーザーがコメントを読みやすくなります。

これが記事の全文です: http://weblogs.manas.com.ar/ary/2007/11/

1
asterite

ここで同様の質問に対するいくつかの回答があります: インターフェイスvs基本クラス

1
razlebe

今まで見た中で最高のJavaコードは、ほとんどすべてのオブジェクト参照をクラスのインスタンスではなくインターフェイスのインスタンスとして定義しました。これは、柔軟性と変更のために設計された高品質のコードの強力な兆候です。

1
dongilmore

私はあなたがデザインパターンをよく理解する必要があると思います。

チェックアウト Head First Design Patterns

1
None

C#のインターフェイスは、同じ基本クラスを共有しないクラスのポリモーフィズムを許可する場合にも非常に役立ちます。つまり、多重継承はできないため、インターフェイスを使用してさまざまなタイプを使用できます。また、リフレクションなしで使用するためにプライベートメンバーを公開できるようにする方法(明示的な実装)であるため、オブジェクトモデルをクリーンに保ちながら機能を実装するための良い方法になる場合があります。

例えば:

public interface IExample
{
    void Foo();
}

public class Example : IExample
{
    // explicit implementation syntax
    void IExample.Foo() { ... }
}

/* Usage */
Example e = new Example();

e.Foo(); // error, Foo does not exist

((IExample)e).Foo(); // success
1
justin.m.chase