私はこれが数回言及されているのを見ました、そしてそれが何を意味するのか私ははっきりしません。いつそしてなぜあなたはこれをしますか?
私はインターフェースが何をするのか知っています、しかし私がこれについてはっきりしていないという事実は私が私がそれらを正しく使うのを見逃していると思います。
あなたがしなければならなかった場合それはちょうどそうです:
IInterface classRef = new ObjectWhatever()
IInterface
を実装するクラスを使用することができますか?いつそれをする必要がありますか?私が考えることができる唯一のことはあなたがメソッドを持っていて、それがIInterface
を実装していることを除いてどのオブジェクトが渡されるのかわからない場合です。私はあなたがそれをする必要があるだろうどのくらいの頻度で考えることはできません。
また、インターフェースを実装するオブジェクトを取り込むメソッドをどのように書くことができるでしょうか。それは可能ですか?
この質問に対するいくつかのすばらしい答えがここにあります。それはインタフェースとコードの疎結合、制御の逆転などについてのあらゆる種類の詳細について説明しています。かなり話題の議論がいくつかあるので、私は、インタフェースがなぜ有用であるかを理解するために、物事を少し細分化する機会を得たいと思います。
私が最初にインターフェースに触れ始めたとき、私もそれらの関連性について混乱していました。なぜあなたがそれらを必要としていたのか理解できませんでした。私たちがJavaやC#のような言語を使っているなら、私たちはすでに継承を持っているので、私はインターフェースを弱い形式の継承として見て考えました。 「」ある意味では、インターフェースは一種の弱い継承のようなものと考えることができますが、それを超えて、それらを共通の特性や振る舞いを分類する手段として考えることによって、言語構造としての使用がついにわかりました。潜在的に多くの無関係なクラスのオブジェクト。
たとえば、SIMゲームがあり、次のクラスがあるとします。
class HouseFly inherits Insect {
void FlyAroundYourHead(){}
void LandOnThings(){}
}
class Telemarketer inherits Person {
void CallDuringDinner(){}
void ContinueTalkingWhenYouSayNo(){}
}
明らかに、これら2つのオブジェクトは直接継承に関しては何も共通点がありません。しかし、あなたは彼らが両方とも迷惑であると言うことができます。
私たちのゲームは、彼らが夕食を食べるときにゲームプレーヤーをいらいらさせるある種のランダムなものを持つ必要があるとしましょう。これはHouseFly
、Telemarketer
、またはその両方ですが、単一の関数でどのように両方を許可するのですか。そして、どのようにして、それぞれの異なる種類のオブジェクトに同じように「迷惑なことをする」ように依頼しますか。
実現するための秘訣は、Telemarketer
とHouseFly
の両方が、それらをモデル化するという点では類似していないにもかかわらず、共通の大まかに解釈された振る舞いを共有することです。それでは、両方が実装できるインターフェースを作りましょう。
interface IPest {
void BeAnnoying();
}
class HouseFly inherits Insect implements IPest {
void FlyAroundYourHead(){}
void LandOnThings(){}
void BeAnnoying() {
FlyAroundYourHead();
LandOnThings();
}
}
class Telemarketer inherits Person implements IPest {
void CallDuringDinner(){}
void ContinueTalkingWhenYouSayNo(){}
void BeAnnoying() {
CallDuringDinner();
ContinueTalkingWhenYouSayNo();
}
}
2つのクラスがあり、それぞれ独自の方法で迷惑になる可能性があります。そして、それらは同じ基本クラスから派生し、共通の固有の特性を共有する必要はありません - 彼らは単にIPest
の契約を満たす必要があります - その契約は簡単です。あなたはBeAnnoying
だけです。これに関しては、次のようにモデル化できます。
class DiningRoom {
DiningRoom(Person[] diningPeople, IPest[] pests) { ... }
void ServeDinner() {
when diningPeople are eating,
foreach pest in pests
pest.BeAnnoying();
}
}
ここには、たくさんのダイナーとたくさんの害虫を受け入れるダイニングルームがあります - インターフェースの使い方に注目してください。これは、私たちの小さな世界では、pests
配列のメンバーが実際にはTelemarketer
オブジェクトまたはHouseFly
オブジェクトになる可能性があることを意味します。
ServeDinner
メソッドは、夕食が出されてダイニングルームにいる私たちの人々が食べることになっているときに呼び出されます。私たちの小さなゲームでは、それが私たちの害虫が彼らの仕事をするときです - それぞれの害虫はIPest
インターフェースによっていらいらするように指示されます。このようにして、Telemarketers
とHouseFlys
の両方をそれぞれの方法で簡単に迷惑にさせることができます - DiningRoom
オブジェクトに害虫であるものがあることだけを気にします。他と共通点がない。
この非常に工夫を凝らした疑似コードの例(私が予想していたよりもずっと長くドラッグしたもの)は、インターフェースを使用する可能性があるという観点から、最終的に私を照らし出した種類のものを説明するためのものです。例の愚かさを事前にお詫び申し上げますが、それがあなたの理解に役立つことを願っています。そして、確かに、あなたがここで受け取った他の投稿された答えは本当にデザインパターンと開発方法論における今日のインタフェースの使用の全範囲をカバーしています。
私が学生に与えた特定の例は、彼らが書くべきであるということです
List myList = new ArrayList(); // programming to the List interface
の代わりに
ArrayList myList = new ArrayList(); // this is bad
これらは短いプログラムでもまったく同じに見えますが、プログラムでmyList
を100回使用し続けると、違いが見え始めることがあります。最初の宣言は、myList
インターフェースによって定義されているList
上のメソッドのみを呼び出すようにします(したがって、ArrayList
固有のメソッドは呼び出されません)。あなたがこのようにインターフェースにプログラムしたならば、後であなたはあなたが本当に必要であると決めることができます
List myList = new TreeList();
そして、あなたはその1つの場所であなたのコードを変える必要があるだけです。 インターフェースにプログラムしたので、残りのコードが実装を変更することによって壊れることは何もしないことをすでに知っています。
メソッドパラメータと戻り値について話しているとき、その利点はさらに明白になります(私は思います)。例を挙げましょう。
public ArrayList doSomething(HashMap map);
そのメソッド宣言は、2つの具象実装(ArrayList
とHashMap
)に結び付けます。そのメソッドが他のコードから呼び出されるとすぐに、これらの型を変更すると、おそらく呼び出し側のコードも変更する必要があることを意味します。インターフェースにプログラムするほうが良いでしょう。
public List doSomething(Map map);
どのようなList
を返すのか、あるいはどのようなMap
がパラメータとして渡されるのかは関係ありません。 doSomething
メソッド内で行った変更によって、呼び出し元のコードを変更することは強制されません。
インターフェースへのプログラミングは、「私はこの機能が必要で、どこから来たのかは気にしません」と言っています。
(Javaでは)List
インターフェースとArrayList
およびLinkedList
具象クラスを検討してください。私が気にしていることが、反復を介してアクセスする必要がある複数のデータ項目を含むデータ構造があることだけであれば、私はList
を選びます(そしてそれは99%の時間です)。リストのどちらかの端から定数時間で挿入/削除する必要があることがわかっている場合は、LinkedList
具象実装を選択します(または Queueを使用します インタフェース)インデックスによるランダムアクセスが必要なことがわかったら、ArrayList
具象クラスを選びます。
クラス間の不要な結合を排除することに加えて、インターフェイスを使用することは、コードを簡単にテスト可能にするための重要な要素です。クラスの操作を定義するインターフェースを作成することで、その機能を使用したいクラスに、実装クラスに直接依存せずにそれを使用できるようにすることができます。後で別の実装を変更して使用することにした場合は、実装がインスタンス化されている部分のコードを変更するだけで済みます。コードの残りは、実装クラスではなくインタフェースに依存するため、変更する必要はありません。
これは単体テストを作成するのに非常に役立ちます。テスト中のクラスでは、インターフェイスに依存し、コンストラクタまたはプロパティ設定メソッドを介してインターフェイスのインスタンスをクラス(または必要に応じてインターフェイスのインスタンスを構築できるようにするファクトリ)に注入します。クラスはそのメソッドで提供された(または作成された)インタフェースを使用します。テストを書きに行くときには、インターフェースを偽造したり偽造したり、単体テストで設定されたデータで応答するインターフェースを提供したりできます。これは、テスト中のクラスが具体的な実装ではなくインタフェースのみを扱うために可能になります。あなたのモッククラスやフェイククラスを含む、インターフェースを実装するクラスならどれでも構いません。
編集:以下は、Erich Gammaが「実装ではなくインタフェースへのプログラム」という引用を説明している記事へのリンクです。
制御の反転を調べる必要があります。
このようなシナリオでは、これを書くことはありません。
IInterface classRef = new ObjectWhatever();
次のように記述します。
IInterface classRef = container.Resolve<IInterface>();
これは、container
オブジェクト内のルールベースのセットアップに入り、実際のオブジェクト(ObjectWhateverなど)を構築します。重要なことは、このルールを別のタイプのオブジェクトを完全に使用するものに置き換えることができ、コードが引き続き機能することです。
IoCをテーブルから外すと、特定の何かを行うオブジェクトと通信できることを知っているコードを書くことができますが、どのタイプのオブジェクトまたはそれを行うかはできません。
これは、パラメーターを渡すときに役立ちます。
かっこで囲まれた質問「インターフェイスを実装するオブジェクトを受け取るメソッドをどのように書くことができますか?それは可能ですか?」、C#では、次のようにパラメータータイプにインターフェイスタイプを使用します。
public void DoSomethingToAnObject(IInterface whatever) { ... }
これは、「特定のことを行うオブジェクトと対話する」ことを意味します。上記で定義されたメソッドは、オブジェクトに何を期待するかを知っており、IInterfaceですべてを実装しますが、どのタイプのオブジェクトであるかは関係なく、コントラクトを遵守すること、つまりインターフェイスとは関係ありません。
たとえば、あなたはおそらく電卓に精通しており、おそらくあなたの日々にかなりの数を使用しているでしょうが、ほとんどの場合それらはすべて異なっています。一方、あなたは標準的な電卓がどのように機能するかを知っているので、他のどの電卓にもない特定の機能を使用できない場合でも、すべてを使用できます。
これがインターフェースの美しさです。特定の動作を期待できるオブジェクトが渡されることを知っているコードを書くことができます。どんな種類のオブジェクトであるかを気にせず、必要な動作をサポートするだけです。
具体的な例を挙げましょう。
Windowsフォーム用にカスタム構築された翻訳システムがあります。このシステムは、フォーム上のコントロールをループし、それぞれのテキストを翻訳します。システムは、テキストプロパティを持つコントロールのタイプなどの基本的なコントロールの処理方法を知っていますが、基本的なものについては不十分です。
これで、コントロールは制御できない事前定義クラスから継承されるため、次の3つのいずれかを実行できます。
だから私たちはnrをしました。 3.すべてのコントロールはILocalizableを実装します。ILocalizableは、「自身」を翻訳テキスト/ルールのコンテナに翻訳する1つのメソッドを提供するインターフェイスです。そのため、フォームはどの種類のコントロールが見つかったかを知る必要はなく、特定のインターフェイスを実装し、コントロールをローカライズするために呼び出すことができるメソッドがあることを知っているだけです。
インターフェースへのプログラミングは、Javaや.NETで見られるような抽象インターフェースとはまったく関係ありません。それはOOP概念でさえありません。
それが本当に意味するのは、オブジェクトやデータ構造の内部構造をいじってはいけないということです。データと対話するには、抽象プログラムインターフェース(API)を使用してください。 JavaまたはC#では、生のフィールドアクセスの代わりにパブリックプロパティとメソッドを使用することを意味します。 Cでは、生のポインタの代わりに関数を使うという意味です。
編集:そしてデータベースとそれは直接テーブルアクセスの代わりにビューとストアドプロシージャを使用することを意味します。
インタフェースへのコード実装はJavaとは何の関係もなく、そのインタフェース構成体もありません
この概念はPatterns/Gang of Fourの本の中で際立つようになりましたが、おそらくそれよりずっと前にあったでしょう。 この概念は確かにJavaが存在する以前から存在していました。
Java Interface構成体は(特に)このアイデアを助けるために作成されたもので、人々は本来の意図ではなく意味の中心として構成体に集中しすぎています。ただし、それが、Java、C++、C#などのパブリックメソッドとプライベートメソッドおよび属性がある理由です。
それは単にオブジェクトやシステムのパブリックインターフェースと対話することを意味します。心配したり、それが内部で行うことをどのように行うのかを予測しないでください。実装方法については心配しないでください。オブジェクト指向コードでは、それが私たちがパブリックかプライベートかのメソッド/属性を持っている理由です。プライベートメソッドはクラス内で内部的にのみ使用されるため、パブリックメソッドを使用することを意図しています。それらはクラスの実装を構成し、パブリックインタフェースを変更することなく必要に応じて変更することができます。機能に関しては、クラスのメソッドは、同じパラメータで呼び出すたびに同じ期待される結果で同じ操作を実行すると仮定します。作成者は、人々がどのようにそれと対話するかを壊すことなく、クラスがどのように機能するか、その実装を変更することができます。
また、Interfaceコンストラクトを使用しなくても、実装ではなくインタフェースにプログラミングできます。Interface構文を持たないC++では、実装ではなくインタフェースにプログラミングできます。 2つの大規模エンタープライズシステムは、システム内部のオブジェクトのメソッドを呼び出すのではなく、パブリックインターフェイス(コントラクト)を介して対話する限り、はるかに堅牢に統合できます。同じ入力パラメータが与えられた場合、インタフェースは常に同じ期待された方法で反応することが期待されています。実装ではなくインタフェースに実装されている場合。この概念はさまざまな場所で機能します。
Javaインタフェースは、「実装ではなくインタフェースへのプログラム」の概念と関係があるという考えを揺さぶってください。それらは概念を適用するのを助けることができます、しかしそれらは概念ではありませんない。
インターフェースがどのように機能するのかを理解していても、いつ使用するのか、またどのような利点があるのかわからないようです。以下は、インターフェースが意味をなす場合の例です。
// if I want to add search capabilities to my application and support multiple search
// engines such as google, yahoo, live, etc.
interface ISearchProvider
{
string Search(string keywords);
}
それから私はGoogleSearchProvider、YahooSearchProvider、LiveSearchProviderなどを作成することができます.
// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
void Download(string url)
}
// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
Bitmap LoadImage(string filename)
}
それからJpegImageLoader、GifImageLoader、PngImageLoaderなどを作成します。
ほとんどのアドインとプラグインシステムはインターフェースから動作します。
もう1つの一般的な用途はRepositoryパターンです。さまざまなソースから郵便番号のリストをロードしたいとします。
interface IZipCodeRepository
{
IList<ZipCode> GetZipCodes(string state);
}
私のWebアプリケーションでは、Sqlデータベースの準備ができる前に何かを起動して実行できるように、XMLリポジトリを早い段階で作成することがよくあります。データベースの準備が整ったら、XMLバージョンを置き換えるためのSQLRepositoryを作成します。私のコードの残りの部分は、インターフェイスからすぐに実行されるため変更されていません。
メソッドは以下のようなインターフェースを受け入れることができます。
PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
{
Console.WriteLine(zipCode.ToString());
}
}
似たようなクラスがいくつもある場合は、コードがはるかに拡張可能になり、保守が容易になります。私はジュニアプログラマーなので、私は専門家ではありませんが、似たようなものを必要とするプロジェクトを終えたところです。
私は医療機器を実行しているサーバーと通信するクライアントサイドソフトウェアを開発しています。私達は顧客が時々設定しなければならないある新しい部品があるこの装置の新しいバージョンを開発している。新しいコンポーネントには2つのタイプがあり、それらは異なりますが、それらも非常に似ています。基本的に、2つのconfigフォーム、2つのリストクラス、2つすべてを作成する必要がありました。
実際のロジックのほとんどすべてを保持するコントロール型ごとに抽象基本クラスを作成し、次に派生型を作成して2つのコンポーネント間の違いを処理するのが最善だと思いました。しかし、常に型について心配しなければならないのであれば、基本クラスはこれらのコンポーネントに対して操作を実行できませんでした(そうですね、すべてのメソッドでifステートメントやswitchがあったはずです)。 。
私はこれらのコンポーネントのための単純なインターフェースを定義し、そしてすべての基本クラスはこのインターフェースと対話します。今私が何かを変更したとき、それはほとんどどこでも「うまくいく」だけで、コードの重複はありません。
Javaでプログラムする場合、JDBCはその好例です。 JDBCはインタフェースのセットを定義しますが、実装については何も言いません。あなたのアプリケーションはこのインタフェースのセットに対して書くことができます。理論的には、JDBCドライバを選択すればアプリケーションは正常に動作します。より速いまたは「よりよい」またはより安価なJDBCドライバがあることを発見した場合、または何らかの理由で、再び理論上はプロパティファイルを再設定することができます。
インターフェイスへのプログラミングは素晴らしいです、それは疎結合を促進します。 @lassevkが述べたように、コントロールの反転はこれをうまく利用したものです。
さらに、SOLIDプリンシパルを調べます。 こちらがビデオシリーズです
それはハードコードされた(強く結合された例)を通過し、そしてインターフェースを調べ、最後にIoC/DIツール(NInject)に進みます。
私はこの質問に遅れて来ていますが、ここで言及したいのは、GoF(Gang of Four)Design Patternsの本の中で、「実装ではなくインターフェースへのプログラム」という行がよく議論されていることです。
それは、pに述べました。 18:
実装ではなくインタフェースにプログラムします
特定の具象クラスのインスタンスとして変数を宣言しないでください。代わりに、抽象クラスによって定義されたインタフェースだけにコミットしてください。この本のデザインパターンの共通のテーマであることがわかります。
そしてそれ以上に、それは以下で始まりました:
抽象クラスによって定義されたインタフェースに関してのみオブジェクトを操作することには2つの利点があります。
- オブジェクトがクライアントが期待するインターフェースに準拠している限り、クライアントは、使用する特定のタイプのオブジェクトを認識しません。
- クライアントは、これらのオブジェクトを実装するクラスを認識していません。クライアントはインタフェースを定義する抽象クラスについてしか知りません。
つまり、アヒル用のquack()
メソッド、そして犬用のbark()
メソッドを持つようにクラスを書かないでください。これらはクラス(またはサブクラス)の特定の実装には特殊すぎるためです。代わりに、giveSound()
やmove()
のように基本クラスで使用するのに十分一般的な名前を使用してメソッドを作成します。そうすれば、それらはアヒル、犬、さらには車にも使用できます。オブジェクトに送信する正しいメッセージを発行する前に、.giveSound()
またはquack()
のどちらを使用するか、あるいは型を決定するかを検討するのではなく、bark()
を使用してください。
そこにはたくさんの説明がありますが、もっと簡単にするためです。例えばList
を取ります。 asを使ってリストを実装することができます:
インターフェースを構築することによって、List
と言います。あなたは、Listの定義、あるいは実際にはList
が何を意味するのかに関してのみコーディングします。
あなたは内部的にarray
実装を言うどんなタイプの実装も使うことができます。しかし、何らかの理由で実装を変更したいとしたら、バグやパフォーマンスと言ってください。その後、宣言List<String> ls = new ArrayList<String>()
をList<String> ls = new LinkedList<String>()
に変更するだけです。
コード内の他の場所にはありません。他に何か変更する必要がありますか。他のすべてがList
の定義に基づいて構築されているからです。
すでに選択されている回答(およびここでのさまざまな有益な投稿)に加えて、 Head First Design Patterns のコピーを入手することを強くお勧めします。それは非常に読みやすく、あなたの質問に直接答え、なぜそれが重要なのかを説明し、そしてあなたがあなたがその原則(そして他のもの)を利用するために使用できる多くのプログラミングパターンを示すでしょう。
既存の投稿に追加するために、開発者が別々のコンポーネントで同時に作業するとき、時々インターフェイスへのコーディングは大きなプロジェクトに役立ちます。必要なのは、インターフェイスを事前に定義してそれらにコードを書くことであり、他の開発者は実装しているインターフェイスにコードを書くことです。
それは単体テストにも適しています、あなたはそれに依存するクラスにあなた自身のクラス(インターフェースの要件を満たす)を注入することができます
したがって、これを正しく行うために、インターフェースの利点は、特定のクラスからメソッドの呼び出しを分離できることです。代わりに、インターフェースのインスタンスを作成します。ここでは、そのインターフェースを実装するクラスを選択したクラスから実装が提供されます。このように、似ているがわずかに異なる機能を持ち、場合によっては(インタフェースの意図に関連する場合)、多くのクラスを持つことができます。
例えば、私は移動インターフェースを持つことができます。何かを '移動'させるメソッドと、移動インタフェースを実装する任意のオブジェクト(Person、Car、Cat)を渡して移動を指示できます。メソッドがなければ、すべてのクラスの種類を知ることができます。
C++の説明.
インターフェースをクラスのパブリックメソッドと考えてください。
その後、独自の機能を実行するために、これらのパブリックメソッドに「依存する」テンプレートを作成できます(クラスパブリックインターフェイスで定義された関数呼び出しを行います)。このテンプレートはVectorクラスのようなコンテナであり、それが依存するインタフェースは検索アルゴリズムです。
関数/インターフェースVectorを定義する任意のアルゴリズムクラスは、(誰かが元の返事で説明したように) 'コントラクト'を満足させるための呼び出しを行います。アルゴリズムは同じ基底クラスである必要すらありません。唯一の要件は、Vectorが依存する関数/メソッド(インターフェース)がアルゴリズムで定義されていることです。
これらすべての要点は、Vectorが依存するインターフェース(バブル検索、順次検索、クイック検索)が提供されている限り、任意の異なる検索アルゴリズム/クラスを提供できるということです。
検索アルゴリズムが依存するインタフェース/規約を満たすことで、Vectorと同じ検索アルゴリズムを利用する他のコンテナ(リスト、キュー)を設計することもできます。
大きくなり過ぎた継承ツリーで問題を過度に複雑にすることなく、作成したすべての新しいオブジェクトに固有のアルゴリズムを何度も何度も書くことができるので、時間を節約できます(OOP原則「コード再利用」)。
物事がどのように機能するかについての「見逃し」に関しては。これは標準TEMPLATEライブラリのフレームワークの大部分がどのように動作するかであるためです。
もちろん、継承クラスと抽象クラスを使用すると、インタフェースへのプログラミング方法が変わります。しかし、原則は同じです、あなたのパブリック関数/メソッドはあなたのクラスのインターフェースです。
これは大きなトピックであり、デザインパターンの基本原則の1つです。
抽象化に依存していない場合でも、インターフェイスにプログラムすることは有利です。
インターフェースへのプログラミングは、文脈上適切なオブジェクトのサブセットを使用することを強制します
たとえば、Person
およびFriend
インターフェースを実装するEmployee
クラスを考えてみましょう。
class Person implements AbstractEmployee, AbstractFriend {
}
その人の誕生日のコンテキストでは、その人がFriend
のように扱われるのを防ぐために、Employee
インターフェースにプログラムします。
function party() {
const friend: Friend = new Person("Kathryn");
friend.HaveFun();
}
人の仕事の文脈では、職場の境界があいまいになるのを防ぐために、Employee
インターフェースにプログラムします。
function workplace() {
const employee: Employee = new Person("Kathryn");
employee.DoWork();
}
すばらしいです。私たちはさまざまな状況で適切に振舞い、私たちのソフトウェアはうまく機能しています。
将来的には、私たちの事業が犬と一緒に働くように変わっても、私たちはソフトウェアをかなり簡単に変えることができます。まず、Dog
とFriend
の両方を実装するEmployee
クラスを作成します。その後、安全にnew Person()
をnew Dog()
に変更します。両方の関数に数千行のコードが含まれていても、次のことが当てはまるため、その簡単な編集は機能します。
party
は、Friend
のPerson
サブセットのみを使用します。workplace
は、Employee
のPerson
サブセットのみを使用します。Dog
は、Friend
とEmployee
の両方のインターフェースを実装します。一方、party
またはworkplace
のいずれかがPerson
に対してプログラムされると、両方がPerson
固有のコードを持つ危険性があります。 Person
からDog
に変更すると、Person
がサポートしていないDog
特有のコードを切り出すためにコードをくまなく調べる必要があります。
モラル:インターフェースへのプログラミングは、コードが適切に動作して変更の準備ができるようにするのに役立ちます。また、抽象化に依存するようにコードを準備します。これにより、さらに多くの利点がもたらされます。
プラグインで拡張できる 'Zebra'という製品があるとします。それはあるディレクトリでDLLを探すことでプラグインを見つけます。それはそれらすべてのDLLをロードし、リフレクションを使ってIZebraPlugin
を実装するクラスを見つけ、そしてそのインターフェースのメソッドを呼び出してプラグインと通信します。
これにより、特定のプラグインクラスから完全に独立したものになります - クラスが何であるかは関係ありません。それはそれらがインターフェース仕様を満たすことを気にするだけです。
インタフェースは、このように拡張性のポイントを定義する方法です。インターフェースと対話するコードは、より疎結合になっています - 実際、他の特定のコードとはまったく結合していません。それは何年も後にオリジナルの開発者に会ったことがない人々によって書かれたプラグインと相互運用することができます。
代わりに、仮想関数を持つ基本クラスを使用することもできます - すべてのプラグインは基本クラスから派生します。しかし、クラスは1つの基本クラスしか持つことができないため、これははるかに制限的です。一方、クラスは任意の数のインタフェースを実装できます。
Javaでは、これらの具象クラスはすべてCharSequenceインタフェースを実装しています。
CharBuffer、String、StringBuffer、StringBuilder
これらの具象クラスはObject以外の共通の親クラスを持たないので、それらをそれぞれ表す、またはそれを操作する、文字の配列と関係があるという事実以外には、それらを関連付けるものは何もありません。たとえば、Stringオブジェクトがインスタンス化されるとStringの文字は変更できませんが、StringBufferまたはStringBuilderの文字は編集できます。
それでも、これらのクラスはそれぞれ、CharSequenceインタフェースメソッドを適切に実装できます。
char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()
場合によっては、Stringを受け付けるJavaクラスライブラリクラスは、CharSequenceインタフェースを受け付けるように修正されています。つまり、Stringオブジェクトを抽出する代わりに(新しいオブジェクトインスタンスをインスタンス化することを意味する)StringBuilderのインスタンスがある場合は、代わりにCharSequenceインタフェースを実装するのでStringBuilder自体を渡すことができます。
一部のクラスが実装するAppendableインターフェースは、基礎となる具象クラスオブジェクトインスタンスのインスタンスに文字を追加できるような状況でも、ほぼ同じ種類の利点があります。これらの具象クラスはすべてAppendableインターフェースを実装しています。
BufferedWriter、CharArrayWriter、CharBuffer、FileWriter、FilterWriter、LogStream、OutputStreamWriter、PipedWriter、PrintStream、PrintWriter、StringBuffer、StringBuilder、StringWriter、Writer
簡単に言えば...新しいクラスSwimmerを書いて機能swim()を追加し、Dogというクラスのオブジェクトを使用する必要がある場合そして、このDogクラスはswim()を宣言するインタフェースAnimalを実装しています[理解を深めるために...あなたは私が話していることに関してダイアグラムを描くことがあります]。階層の最上部(動物)は非常に抽象的ですが、最下部(犬)は非常に具体的です。私が「インターフェースへのプログラミング」について考える方法は、私がSwimmerクラスを書いているときに、この場合はAnimalオブジェクトであるその階層のはるか上にあるインターフェースに対して自分のコードを書きたいということです。インターフェースは実装の詳細から解放されているので、コードは疎結合になります。実装の詳細は時間とともに変更することができますが、やり取りしているのはインターフェースであり実装ではないため、残りのコードには影響しません。あなたは、実装がどのようなものであるかを気にしません...あなたが知っているのは、インターフェースを実装するクラスがあるということだけです。
短編小説:郵便配達人は自宅で家に帰り、配達のために書かれた住所が含まれている(手紙、書類、小切手、ギフトカード、申請書、ラブレター)カバーを受け取る。
カバーがないと仮定して、郵便配達人に自宅で家に帰ってすべてのものを受け取り、郵便配達人が混乱する可能性がある他の人に配達するよう依頼します。
(私たちの物語ではインターフェースです)それでカバーでそれを包むより良いそしてそれから彼は彼の仕事をうまくやるでしょう。
今郵便配達の仕事はカバーだけを受け取り、配達することです..(彼はカバーの中にあるものを気にしないでください)。
実際の型ではなくinterface
の型を作成します。実際の型で実装してください。
インターフェースに作成すると、コンポーネントはを取得します。残りのコードには簡単に収まります
私はあなたに例を挙げます。
以下のようにAirPlaneインターフェースがあります。
interface Airplane{
parkPlane();
servicePlane();
}
次のようにPlaneのControllerクラスにメソッドがあるとします。
parkPlane(Airplane plane)
そして
servicePlane(Airplane plane)
あなたのプログラムに実装されています。それはあなたのコードを壊すしません。つまり、引数をAirPlane
として受け付ける限り、変更する必要はありません。
実際の型、flyer
、highflyr
、fighter
などにかかわらず、どの飛行機でも受け入れることができるからです。
また、コレクションでは:
List<Airplane> plane;
//あなたの全ての飛行機を取ります。
次の例はあなたの理解を明確にするでしょう。
それを実装する戦闘機があります。
public class Fighter implements Airplane {
public void parkPlane(){
// Specific implementations for fighter plane to park
}
public void servicePlane(){
// Specific implementatoins for fighter plane to service.
}
}
HighFlyerや他のクラスでも同じことが言えます。
public class HighFlyer implements Airplane {
public void parkPlane(){
// Specific implementations for HighFlyer plane to park
}
public void servicePlane(){
// specific implementatoins for HighFlyer plane to service.
}
}
さて、あなたのコントローラクラスがAirPlane
を数回使っているとしましょう。
以下のようにあなたのControllerクラスがControlPlaneであるとします。
public Class ControlPlane{
AirPlane plane;
// so much method with AirPlane reference are used here...
}
ここに魔法が来る
新しいAirPlane
型のインスタンスを必要なだけ作成でき、変更はしません。
ControlPlane
クラスのコード。
インスタンスを追加できます。
JumboJetPlane // implementing AirPlane interface.
AirBus // implementing AirPlane interface.
以前に作成した型のインスタンスも削除できます。
インターフェースはコントラクトのようなもので、インプリメンテーションクラスにコントラクト(インターフェース)で記述されたメソッドを実装させたい場合があります。 Javaは多重継承を提供しないので、「インターフェースへのプログラミング」は多重継承を達成するための良い方法です。
他のクラスBをすでに拡張しているクラスAがあるが、そのクラスAに特定のガイドラインに従うか、または特定の契約を実装することを希望する場合は、「プログラミングからインターフェースへ」の戦略によって実行できます。
インタフェースへのプログラムはインタフェースで定義された規約の実装をシームレスに変更することを可能にします。それは契約と特定の実装の間の疎結合を可能にします。
IInterface classRef = new ObjectWhatever()
あなたはIInterfaceを実装する任意のクラスを使うことができますか?いつそれをする必要がありますか?
良い例として、このSEの質問を見てください。
インターフェイスを使用するとパフォーマンスが低下しますか?
もしそんなに?
はい。数秒でわずかなパフォーマンスのオーバーヘッドが発生します。しかし、あなたのアプリケーションが動的にインターフェースの実装を変更する必要がある場合は、パフォーマンスへの影響を心配しないでください。
2ビットのコードを維持しなくても、どうすればそれを回避できますか?
あなたのアプリケーションがそれらを必要とするならば、インターフェースの多重実装を避けようとしないでください。ある特定の実装とインターフェースが密接に関連していない場合は、ある実装を別の実装に変更するためにパッチを適用する必要があります。
1つの良い使用例:戦略パターンの実装
Q: - ... "あなたはインターフェースを実装するクラスを使うことができますか?"
A: - はい。Q:-... "いつそれをする必要がありますか?"
A: - インターフェースを実装するクラスが必要になるたびに。
注: クラスによって実装されていないインターフェースをインスタンス化できませんでした - True。
AnIntf anInst = new Aclass();
// これを行うことができますの場合のみ AclassはAnIntfを実装しています。
// anInstはAclassを参照します。
注意:
これで、BclassとCclassが同じDintfを実装した場合に何が起こるのか理解できました。
Dintf bInst = new Bclass();
// now we could call all Dintf functions implemented (defined) in Bclass.
Dintf cInst = new Cclass();
// now we could call all Dintf functions implemented (defined) in Cclass.
私たちが持っているもの:
同じインタフェースプロトタイプ(インタフェース内の関数名)、および異なる実装を呼び出します。
参考文献:
プロトタイプ - wikipedia
私はinterface
sが言語の中で最も重要なことであることを保持していません:それはクラス継承を使うのがより一般的です。しかしとにかくそれらは重要です!
例えば(これはJava
コードですが、単にC#
や他の多くの言語に適応させることができます)
interface Convertable<T> {
T convert();
}
public class NumerableText implements Convertable<Integer> {
private String text = "";
public NumerableText() { }
public NumerableText(String text) {
this.text = text;
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public Integer convert() {
return this.text.hashCode();
}
}
public class NumerableTextArray implements Convertable<Integer> {
private String[] textArray = "";
public NumerableTextArray() { }
public NumerableTextArray(String[] textArray) {
this.textArray = textArray;
}
public String[] getTextArray() {
return this.textArray;
}
public void setTextArray(String[] text) {
this.textArray = textArray;
}
public Integer convert() {
Integer value = 0;
for (String text : textArray)
value += text.hashCode();
return value;
}
}
public class Foo {
public static void main() {
Convertable<Integer> num1 = new NumerableText("hello");
Convertable<Integer> num2 = new NumerableTextArray(new String[] { "test n°1", "test n°2" });
System.out.println(String.valueOf(num1.convert()));
System.out.println(String.valueOf(num2.convert()));
//Here are you two numbers generated from two classes of different type, but both with the method convert(), which allows you to get that number.
}
}
まずいくつかの定義から始めましょう。
インタフェースn。オブジェクトの操作によって定義されたすべてのシグネチャのセットは、次のインタフェースと呼ばれます。オブジェクト
タイプn。特定のインターフェース
上記で定義されたインターフェースの簡単な例は、query()
、commit()
、close()
などのPDOオブジェクトメソッドすべてで、全体としてではなく、個別ではありません。これらの方法、すなわちそのインターフェースは、オブジェクトに送信することができるメッセージ、要求の完全なセットを定義する。
上で定義された型は特定のインタフェースです。 draw()
、getArea()
、getPerimeter()
などを示すために、作り上げたシェイプインターフェースを使用します。
オブジェクトがデータベースタイプのものであれば、データベースインターフェースのメッセージ/要求、query()
、commit()
などを受け付けることを意味します。オブジェクトはさまざまなタイプにすることができます。データベースオブジェクトは、そのインターフェイスを実装している限りシェイプタイプにすることができます。その場合、これはサブタイプになります。
多くのオブジェクトは、さまざまなインターフェイス/タイプのものであり、そのインターフェイスの実装方法が異なります。これにより、オブジェクトを代用することができ、どちらを使用するかを選択できます。多型としても知られています。
クライアントはインターフェースだけを認識し、実装は認識しません。
そのため、本質的には、インターフェースへのプログラミングは、インターフェースのみを指定したShape
のようなある種の抽象クラス、すなわちdraw()
、getCoordinates()
、getArea()
などを作成することを含みます。三角クラス。 したがって、実装ではなくインタフェースにプログラムします。
「Program to interface」とは、ハードコードを正しく提供しないことを意味します。つまり、以前の機能を損なうことなくコードを拡張する必要があります。前のコードを編集するのではなく、単なる拡張機能です。
私は、難しい質問は簡単な現実の答えで説明されるべきであると強く信じます。そしてソフトウェア設計の分野では、それは非常に重要です。
あなたの家、学校、教会のいずれかのドアを見てください... any建物.
いくつかのドアが右下の危険を持っていると想像してみてください。開いたり閉じたりしているドアと対話するためにお辞儀を
または他の人がちょうど左上に入っています(つまり、一部の小人、障害者、またはKevin Hartは、そのようなドアをあまり面白くなくて使い勝手がよくないでしょう)。 。
だからデザインがキーワードです、他人にプログラムを作成する人間できる開発/使用してください
Interfaces
がすることは、他のジュニア/シニア開発者が巨大なプロジェクトにまたがって物事を簡単にできるようにすることです[1]。他の人たちはそうすることであなたは(理論で)できるだけ滑らかに働くことができる。
[1]どうですか。値の形状を公開することによって。そのため、実際にはドキュメントは必要ありません。コード自体は一目瞭然です(素晴らしい)。
この答えは、概念駆動型ではなく、言語固有のものではありません(結局、人間でコードを書いてツールを作成する)。
また、ここには多くの良い説明的な答えがあるので、この方法を使用したときに気付いた追加情報を含めて、ここで自分の観点を示したいと思います。
単体テスト
ここ2年間、私は趣味のプロジェクトを書いていますが、それについて単体テストを書いていません。約50K行を書いた後、私はそれが単体テストを書くことが本当に必要であることがわかった。私はインターフェースを使用しませんでした(または非常に控えめに)...そして私が最初の単体テストをしたとき、私はそれが複雑であることを知りました。どうして?
私は多くのクラスインスタンスを作らなければならなかったので、クラス変数やパラメータとして入力に使われました。そのため、テストは統合テストのように見えます(すべてが結び付いているため、クラスの完全な「フレームワーク」を作成する必要があります)。
インターフェースへの恐怖そこで、インターフェースを使うことにしました。私が恐れていたのは、すべての機能を(使用されているすべてのクラス内の)いたるところに複数回実装しなければならなかったことです。ある意味でこれは事実ですが、継承を使うことでそれはかなり減らすことができます。
インターフェースと継承の組み合わせ組み合わせがとても良いことがわかりました。とても簡単な例を挙げます。
public interface IPricable
{
int Price { get; }
}
public interface ICar : IPricable
public abstract class Article
{
public int Price { get { return ... } }
}
public class Car : Article, ICar
{
// Price does not need to be defined here
}
このようにコードをコピーする必要はありませんが、それでも車をインタフェースとして使用するという利点があります(ICar)。