メソッドなどの実装を強制することは理解していますが、なぜそれらを使用したいのか理解できません。なぜ私がこれを実装したいのかについて、誰かが私に良い例や説明を与えることができますか.
19 に答える
特定の例:インターフェースは、他の人のコードが満たさなければならない契約を指定する良い方法です。
コードのライブラリを作成する場合、特定の動作セットを持つオブジェクトに対して有効なコードを作成することがあります。最善の解決策は、インターフェイスでこれらの動作を指定し (実装ではなく、説明のみ)、ライブラリ コードでそのインターフェイスを実装するオブジェクトへの参照を使用することです。
次に、ランダムな人がやって来て、そのインターフェイスを実装するクラスを作成し、そのクラスのオブジェクトをインスタンス化してライブラリ コードに渡し、それが機能することを期待します。注: もちろん、インターフェイスの意図を無視してインターフェイスを厳密に実装することは可能です。そのため、単にインターフェイスを実装するだけでは、動作が保証されるわけではありません。愚か者は常に道を見つける!:-)
もう 1 つの具体的な例:協力しなければならない異なるコンポーネントに取り組んでいる 2 つのチーム。2 つのチームが 1 日目に話し合い、一連のインターフェイスについて合意した場合は、それぞれ別の道を進み、それらのインターフェイスにコンポーネントを実装できます。チーム A は、チーム B のコンポーネントをテスト用にシミュレートするテスト ハーネスを作成できます。また、その逆も可能です。並行開発、およびバグの減少。
重要な点は、不要な詳細を無視してコードを記述できるように、インターフェイスが抽象化のレイヤーを提供することです。
ほとんどの教科書で使用されている標準的な例は、ルーチンの並べ替えです。任意の 2 つのオブジェクトを比較する方法がある限り、任意のクラスのオブジェクトを並べ替えることができます。IComparable
したがって、インターフェイスを実装することで、任意のクラスをソート可能にすることができます。これにより、2 つのインスタンスを比較するためのメソッドを実装する必要があります。すべての並べ替えルーチンは、IComparable オブジェクトへの参照を処理するように記述されているため、IComparable を実装するとすぐに、クラスのオブジェクトのコレクションに対してこれらの並べ替えルーチンを使用できます。
インターフェイスを理解する最も簡単な方法は、さまざまなオブジェクトが COMMON 機能を公開できるようにすることです。これにより、プログラマーはインターフェイスにプログラムするより単純で短いコードを書くことができ、オブジェクトがそのインターフェイスを実装している限り、それは機能します。
例 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 式をコレクションに適用できるようになりました。ここでもインターフェイスが非常に便利な理由は、すべてのコレクションに共通点があり、それらを移動できるからです。各コレクションは異なる方法 (リンクされたリストと配列) を介して移動できますが、実装が隠され、インターフェイスの消費者とは無関係であることがインターフェイスの利点です。MoveNext() は、コレクション内の次のアイテムを提供します。それがどのように行われるかは問題ではありません。かなりいいですね。
例 3: 独自のインターフェイスを設計している場合、1 つの質問だけを自問する必要があります。これらの共通点は何ですか?オブジェクトが共有するすべてのものを見つけたら、それらのプロパティ/メソッドをインターフェイスに抽象化して、各オブジェクトが継承できるようにします。次に、1 つのインターフェイスを使用して複数のオブジェクトに対してプログラミングできます。
そしてもちろん、私のお気に入りの C++ ポリモーフィックの例、動物の例を挙げなければなりません。すべての動物は特定の特徴を共有しています。彼らは動くことができ、話すことができ、それらはすべて名前を持っているとしましょう。すべての動物に共通するものを特定したので、それらの性質を IAnimal インターフェイスに抽象化できます。次に、このインターフェイスを実装する Bear オブジェクト、Owl オブジェクト、および Snake オブジェクトを作成します。同じインターフェイスを実装するさまざまなオブジェクトを一緒に格納できる理由は、インターフェイスが 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 つにすぎません。
繰り返しになりますが、インターフェイスで最も重要なことは、異なるオブジェクトに対して同じ方法でプログラムできるように、オブジェクトに共通するものは何かということです。時間を節約し、より柔軟なアプリケーションを作成し、複雑さ/実装を隠し、実世界のオブジェクト/状況をモデル化するなど、多くのメリットがあります。
お役に立てれば。
インターフェイスは契約を定義します。これがキーワードです。
プログラムでコントラクトを定義する必要があるときにインターフェイスを使用しますが、そのコントラクトを実行する限り、そのコントラクトを満たすクラスの残りのプロパティはあまり気にしません。
それでは、例を見てみましょう。リストをソートする機能を提供するメソッドがあるとします。まず、リストとは何ですか? リストをソートするためにどの要素が保持されているか本当に気にしますか? あなたの答えはノーでなければなりません....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 を返すメソッドを比較可能にするために実装する必要があることを指定します。これでコントラクトが定義されました。今のところ、オブジェクト自体ではなく、コントラクトについては気にしないので、次のことを実行できます。
IComparable comp1 = list.GetItem(i) as IComparable;
if (comp1.Compare(list.GetItem(i+1)) < 0)
swapItem(list,i, i+1)
PS:例が少し素朴であることは知っていますが、例です...
典型的な例の 1 つがプラグイン アーキテクチャです。開発者 A はメイン アプリを作成し、開発者 B、C、および D によって作成されたすべてのプラグインが、開発者のアプリが期待するものに準拠していることを確認したいと考えています。
When you need different classes to share same methods you use Interfaces.
車のペダルはインターフェースを実装しています。私は米国出身で、道路の右側を運転しています。私たちのハンドルは車の左側にあります。マニュアルトランスミッションのペダルは、左から順にクラッチ→ブレーキ→アクセル。アイルランドに行った時は運転が逆。車のハンドルは右側にあり、道路の左側を走行します...しかし、ペダル、ああ、ペダル...それらは同じインターフェースを実装しました...3つのペダルはすべて同じ順序でした...クラスが異なっていて、クラスが動作するネットワークが異なっていたとしても、私はまだペダルインターフェースに慣れていました. 私の脳は、他のすべての車と同じように、この車で私の筋肉を呼び出すことができました.
私たちがなくてはならない、数多くの非プログラミング インターフェイスについて考えてみてください。次に、自分の質問に答えます。
ポリモーフィズムをうまく利用することを期待するオブジェクト指向システムでは、インターフェースは絶対に必要です。
古典的な例は、Move() メソッドを持つ IVehicle かもしれません。IVehicle を実装するクラス Car、Bike、および Tank を使用できます。それらはすべて Move() が可能であり、Move() ができるように、対象の車両の種類を気にしないコードを作成できます。
void MoveAVehicle(IVehicle vehicle)
{
vehicle.Move();
}
インターフェイスはポリモーフィズムの一種です。例:
ロギング コードを書きたいとします。ロギングはどこかに行きます (おそらく、ファイル、またはメイン コードが実行されているデバイスのシリアル ポート、またはソケット、または /dev/null のように破棄されます)。どこかはわかりません。ロギング コードのユーザーは、それを自由に判断できる必要があります。実際、ロギング コードは気にしません。バイトを書き込むことができるものが必要なだけです。
それで、「バイトを書き込むことができるもの」と呼ばれるインターフェースを発明します。ロギング コードには、このインターフェイスのインスタンスが与えられます (おそらく実行時に、おそらくコンパイル時に構成されます。それはまだポリモーフィズムであり、種類が異なるだけです)。インターフェイスを実装する 1 つ以上のクラスを記述し、ロギング コードが使用するクラスを変更するだけで、ロギング先を簡単に変更できます。他の誰かが、自分のコードを変更することなく、インターフェースの独自の実装を作成することで、ロギングの行き先を変更できます。これが基本的にポリモーフィズムの意味です。特定の方法でオブジェクトを使用するのに十分なだけ知っている一方で、知る必要のないすべての点でオブジェクトを変更できるようにすることです。インターフェイスは、知っておくべきことを説明します。
Cのファイル記述子は基本的に「バイトを読み書きできるもの」であり、ほとんどすべての型付き言語には、標準ライブラリに潜んでいるそのようなインターフェイスがあります:ストリームなど。型指定されていない言語には通常、ストリームを表す非公式の型 (おそらくコントラクトと呼ばれる) があります。したがって、実際には、この特定のインターフェースを実際に自分で発明する必要はほとんどありません。言語が提供するものを使用します。
ロギングとストリームは一例にすぎません。オブジェクトが何をすべきかを抽象的な用語で記述できるが、特定の実装/クラス/その他のものに結び付けたくない場合はいつでもインターフェイスが発生します。
そうする理由はいくつかあります。インターフェースを使用すると、将来、コードのリファクタリング/リライトが必要になったときに備えることができます。また、単純な操作のために標準化された API のようなものを提供することもできます。
たとえば、クイックソートのようなソート アルゴリズムを作成する場合、オブジェクトのリストをソートするために必要なのは、2 つのオブジェクトを正常に比較できることだけです。たとえば、ISortable というインターフェイスを作成すると、オブジェクトを作成するすべてのユーザーが ISortable インターフェイスを実装でき、並べ替えコードを使用できるようになります。
データベース ストレージを使用するコードを記述していて、ストレージ インターフェイスに書き込む場合は、そのコードを後で置き換えることができます。
インターフェースはコードの疎結合を促進するため、柔軟性が向上します。
基本的な 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 操作を実装しているわけではなく、すべてのファイル リーダーにも同じことが当てはまります。
インターフェイスは、オブジェクトが実行できることと、それをどのように使用する必要があるかを定義します...それが何であるかではありません!
私が今まで見た中で最高の Java コードは、ほとんどすべてのオブジェクト参照を、クラスのインスタンスではなくインターフェースのインスタンスとして定義していました。これは、柔軟性と変更のために設計された高品質のコードの強力な兆候です。
In an article in my blog I briefly describe three purposes interfaces have.
Interfaces may have different purposes:
- Provide different implementations for the same goal. The typical example is a list, which may have different implementations for different performance use cases (LinkedList, ArrayList, etc.).
- Allow criteria modification. For example, a sort function may accept a Comparable interface in order to provide any kind of sort criteria, based on the same algorithm.
- Hide implementation details. This also makes it easier for a user to read the comments, since in the body of the interface there are only methods, fields and comments, no long chunks of code to skip.
Here's the article's full text: http://weblogs.manas.com.ar/ary/2007/11/
デザインパターンをよく理解する必要があると思うので、その力を見てください。
おっしゃる通り、インターフェースは誰かに強制的に作ってもらいたい時にいいです。
インターフェイスは、データが特定の形式ではないためにコード内で危険な想定が行われる可能性がある場合に適しています。
たとえば、現在、データをある形式から別の形式に変換するアプリケーションを作成しています。それらが存在し、適切に実装される可能性が高くなるように、それらのフィールドを強制的に配置したいと考えています。いずれにせよデータが必要になる可能性が高いため、別のバージョンが出てきてコンパイルできないかどうかは気にしません。
このため、通常は仮定を立てることができるか、必要なことを行うためにデータを実際に必要としないため、インターフェイスはめったに使用されません。
インターフェースは、単にインターフェースを定義します。後で、インターフェイスをパラメーターとして受け入れるメソッド (より正確には、そのインターフェイスを実装するオブジェクト) を (他のクラスで) 定義できます。このようにして、メソッドは多種多様なオブジェクトで動作できますが、その唯一の共通点は、それらがそのインターフェイスを実装していることです。
まず、抽象化のレイヤーが追加されます。「この関数の場合、このパラメーターは、これらのパラメーターを持つこれらのメソッドを持つオブジェクトでなければなりません」と言うことができます。また、おそらくこれらのメソッドの意味も、何らかの抽象化された用語で設定し、コードについて推論できるようにしたいでしょう。ダックタイプの言語では、無料で入手できます。明示的な構文「インターフェース」は必要ありません。それでも、コントラクト (Design by Contract など) のような一連の概念的なインターフェイスをまだ作成している可能性があります。
さらに、インターフェースは「純粋」ではない目的で使用されることがあります。Java では、多重継承をエミュレートするために使用できます。C++ では、これらを使用してコンパイル時間を短縮できます。
一般に、コード内の結合を減らします。それはいい。
この方法でコードをテストする方が簡単な場合もあります。
物のコレクションを追跡したいとしましょう。このコレクションは、アイテムの追加と削除、アイテムがコレクション内にあるかどうかの確認など、さまざまなことをサポートする必要があります。
その後、メソッド add()、remove()、contains() を使用してインターフェイス ICollection を指定できます。
コレクションの種類 (List、Array、Hash-table、Red-black tree など) を知る必要のないコードは、インターフェイスを実装したオブジェクトを受け入れ、実際の型を知らなくてもそれらを操作できます。
.Net では、基本クラスを作成し、それらのクラスが何らかの形で関連している場合に継承します。たとえば、基本クラスの Person は Employee と Customer に継承できます。個人には、住所フィールド、名前、電話番号などの共通のプロパティがある場合があります。従業員は、独自の部門プロパティを持っている場合があります。顧客は他の排他的なプロパティを持っています。
クラスは .Net の他の 1 つのクラスからしか継承できないため、追加の共有機能にはインターフェイスを使用します。場合によっては、関連のないクラスによってインターフェイスが共有されることがあります。インターフェイスを使用すると、それを実装する他のすべてのクラスによって共有されることを開発者が認識するコントラクトが作成されます。また、これらのクラスにそのすべてのメンバーを実装するように強制します。
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