2009-06-27 3 views
3

私はそれらが契約であると理解しているので、私はそれを契約用語として解釈します。つまり、インタフェースに指定されているものを持っていなければなりません(ファイルを扱うインタフェースのオープン、クローズ、読み取り、書き込み)。主にインターフェイスの機能は、クラスの構築方法を知らずに関数を使用するためのものですか?

しかし、苦労しているのは、クラスが何をすることができなければならないのかを教えてくれるインターフェイスが必要な理由です。

私がインターフェイスのために見ることができる唯一の理由は、それがどのようにビルドされているかを実際に知らなくてもクラスを使用できるようにする大規模なプロジェクトにあります。インタフェースが必要とするものを見ることで、そのインタフェースの使い方を知ることができます。

これは私が私が唯一の作業になるプロジェクトのインターフェースを使う必要があるのか​​、なぜそうすべきなのか疑問に思います。私は見ていないことをそれ以上の用途があると確信しています。

私はあなたがそれで正しいthis questionthis vbforums post

答えて

1

銃を実装する一連のクラスを作成しているとします。あなたはピストル、ライフル、そしてマシンガンを持っているかもしれません。次に、これらのクラスをそれぞれの銃でfire()アクションを実行するような方法で使用することにしたとします。

private Pistol p01; 
private Pistol p02; 
private Rifle r01; 
private MachineGun mg01; 

public void fireAll() { 
    p01.fire(); 
    p02.fire(); 
    r01.fire(); 
    mg01.fire(); 
} 

銃を追加したり削除したりすると、コードをいくつか変更する必要があるので、このようなことはうんざりです。さらに悪いことに、実行時に銃を追加したり取り外したりできるようにしたいとします。それはさらに困難になります。

上記の銃のそれぞれが実装するインターフェイスを作りましょう。銃器と呼んでください。今、私たちはこれを行うことができます。

private Firearm[] firearms; 

public void fireAll() { 
    for (int i = 0; i < firearms.length; ++i) { 
     firearms[i].fire(); 
    } 
} 

これは少し変わっています。

+0

良い例ですが、Firearmという抽象基本クラスについても同じことが言えます。 –

+0

もちろん、インタフェースは抽象基本クラスで定義することができます。これは、インターフェイスのための別の構文を持たないC++でインターフェイスを定義する方法です。 – dewtell

4

から私の仮定や解釈のほとんどを取ったが、契約を、指定したインターフェイスが、実装におけるは大幅に異なる場合があります。

単純な例:Javaのリスト。リストはインターフェースです。 ArrayListとLinkedListの2つの共通の実装があります。それぞれ異なる動作をしますが、同じ契約を尊重します。これは、ArrayListにO(1)(定数)アクセスがあり、LinkedListにO(n)アクセスがあることを意味しています。

O(1)とO(n)の意味をまだ理解していない場合は、Plain english explanation of Big Oをご覧ください。

あなたは(つまりはないか、パブリックAPIではありません何かを)も、独自のコードでこれを行う理由は、以下のとおりです。

  • ユニットテストを容易:あなたは一方のインターフェイスをモックアップすることができますクラスをモックアップすることはできません(または簡単にできません)。
  • を使用して、呼び出しコードに影響を与えずに後で実装を変更することができます。
2

しかし、どのようなイムあなたが知っているwouldntのあなたは、あなたはクラスがすべてで行うことができなければならないものを伝えるインタフェースを持っている必要がありますなぜされて把握に苦労している、あなたはそれを書いて、すでにので、インタフェース仕様?

これは、外部から利用可能なコードを記述しているときにも適しています。この場合、コードライターはInterfaceのユーザーではありません。あなたがユーザーにライブラリを提供している場合は、あなただけのInterfaceを文書化したい、とClassは、コンテキストに基づいて変更したりInterfaceを変更することなく、時間をかけて進化することを可能にします。

+0

このような良い例は、APIが長年にわたって進化してきた、つまりバージョン管理されたインターフェイスを持つWindows APIです。 IMyInterface、IMyInterface2、IMyInterface3。おそらくクライアントコードは、必要な機能を実現するために必要な最低限の共通点を使用し、必要なインターフェイスを実装したすべてのバージョンに対してコードを下位互換性を持たせるという利点があります。 –

0

以前のコードを再訪したい場合は、いくつかのインターフェイスを自分で構築していただき、ありがとうございます。存在する新しいタイプのものを実装したいだけでなく、新しいオブジェクトが持つものを覚えていないことを理解するために、もっとイライラするものはありません。

Javaでは、複数の継承(複数の親オブジェクトを持つオブジェクト)をシミュレートする複数のインタフェースを実装できます。スーパークラスは1つしか拡張できません。

3

インターフェイスは、一緒に動作する必要があるが、可能な限り互いに分離する必要がある2つのクラスがある場合に便利です。一般的な例として、listenersを使用してmodel-view-controllerデザインパターンでモデルとビューを接続する場合があります。

たとえば、ユーザーがログインしてログアウトできるGUIアプリケーションがあるとします。ユーザーがログアウトすると、「現在ログインしています」ラベルを変更し、表示されているすべてのダイアログウィンドウを閉じます。

logOutメソッドを持つUserクラスがあり、logOutが呼び出されると、これらのことがすべて起こるようになります。それを行う1つの方法は、logOut方法は、これらすべてのタスクを処理しています:

これはあなたの Userクラスは今しっかりとあなたのGUIに接続されているので、眉をひそめている
// Bad! 
public void logOut() { 
    userNameLabel.setText("Nobody is logged in"); 
    userProfileWindow.close(); 
} 

Userクラスをダンバーにしてあまりそうしない方が良いでしょう。 userProfileWindowを閉じるのではなく、ユーザーがログアウトしたことをuserProfileWindowに伝え、userProfileWindowは何をしたいのか(自分自身を閉じたいと思う)を行わせるだけでよい。

これを実行する方法は、ユーザがログアウトするときにUserクラスによって呼び出されるというメソッドで汎用UserListenerインターフェイスを作成することです。ユーザーがログインしてログアウトするときを知りたい人は、このインターフェイスを実装します。

public class User { 
    // We'll keep a list of people who want to be notified about logouts. We don't know 
    // who they are, and we don't care. Anybody who wants to be notified will be 
    // notified. 
    private static List<UserListener> listeners; 

    public void addListener(UserListener listener) { 
     listeners.add(listener); 
    } 

    // This will get called by... actually, the User class doesn't know who's calling 
    // this or why. It might be a MainMenu object because the user selected the Log Out 
    // option, or an InactivityTimer object that hasn't seen the mouse move in 15 
    // minutes, who knows? 
    public void logOut() { 
     // Do whatever internal bookkeeping needs to be done. 
     currentUser = null; 

     // Now that the user is logged out, let everyone know! 
     for (UserListener listener: listeners) { 
      listener.loggedOut(this); 
     } 
    } 
} 

// Anybody who cares about logouts will implement this interface and call 
// User.addListener. 
public interface UserListener { 
    // This is an abstract method. Each different type of listener will implement this 
    // method and do whatever it is they need to do when the user logs out. 
    void loggedOut(User user); 
} 

// Imagine this is a window that shows the user's name, password, e-mail address, etc. 
// When the user logs out this window needs to take action, namely by closing itself so 
// this information isn't viewable by other users. To get notified it implements the 
// UserListener interface and registers itself with the User class. Now the User.logOut 
// method will cause this window to close, even though the User.java source file has no 
// mention whatsoever of UserProfileWindow. 
public class UserProfileWindow implements UserListener { 
    public UserProfileWindow() { 
     // This is a good place to register ourselves as interested observers of logout 
     // events. 
     User.addListener(this); 
    } 

    // Here we provide our own implementation of the abstract loggedOut method. 
    public void loggedOut(User user) { 
     this.close(); 
    } 
} 

動作の順序は次のようになります。

  1. アプリケーションが起動し、ユーザーがログインする彼女はUserProfileWindow彼女を開きます。
  2. UserProfileWindowは、それ自身をUserListenerとして追加します。
  3. ユーザーはアイドル状態になり、15分間キーボードまたはマウスに触れません。
  4. InactivityTimerクラス通知とUser.logOutを呼び出します。
  5. User.logOutはモデルを更新し、currentUser変数をクリアします。もし誰かが尋ねれば、誰もログインしていません。
  6. User.logOutリスナーリストでループし、各リスナーでloggedOut()を呼び出します。
  7. UserProfileWindowloggedOut()メソッドが呼び出され、ウィンドウが閉じます。

このUserクラスは、ログアウトイベントについて誰が知っておく必要があるかについて絶対に何も知らないので、これは素晴らしいことです。ユーザー名ラベルを更新する必要があること、プロファイルウィンドウを閉じる必要があることなどは知られていません。後で、ユーザーがログアウトしたときにもっと多くのことを行う必要があると判断した場合は、Userクラスをまったく変更する必要はありません。

リスナーパターンは、インターフェイスが非常に便利な場合の1つの例です。インターフェイスはすべてデカップリングクラスに関するもので、互いにやりとりする必要のあるクラス間の結びつきや依存関係を取り除きますが、コード間に強い結びつきがあってはいけません。

+0

これは私にとって驚くべきことであり、より多くの点で本物のアイ開口窓を使用しているだけです。 しかし、それらをさらに開くには、そのコードスニペットの残りの部分を「動作中」のもので見てみたいと思います。もう少しペーストしていただけますか? :) –

+0

非常に説明してくれてありがとう!私の頭の上の電球の大きさは今、巨大です。このようなインタフェースの使用法は、Javaに特有(またはより一般的)ですか? –

1

車とゴリラの2つのクラスがあるとします。これらの2つのクラスは互いに関係ありません。しかし、物事を押しつぶすことができるクラスがあるとしましょう。代わりに車を取り、それを押しつぶす方法を定義して、ゴリラを取り、それを押しつぶす別のメソッドを持つ、あなたはICrushableと呼ばれるインタフェースを作る...

interface ICrushable 
{ 
    void MakeCrushingSound(); 
} 

今、あなたはあなたの車を持っているし、あなたのことができますゴリラはICrushableとあなたの車ICrushableを実装して、破砕機は、代わりに車やゴリラのICrushableを操作することができます...

public class Crusher 
{ 
    public void Crush(ICrushable target) 
    { 
    target.MakeCrushingSound(); 
    } 
} 

public class Car : ICrushable 
{ 
    public void MakeCrushingSound() 
    { 
     Console.WriteLine("Crunch!"); 
    } 
} 

public class Gorilla : ICrushable 
{ 
    public void MakeCrushingSound() 
    { 
     Console.WriteLine("Squish!!"); 
    } 
} 

static void Main(string[] args) 
{ 
    ICrushable c = new Car();  // get the ICrushable-ness of a Car 
    ICrushable g = new Gorilla(); // get the ICrushable-ness of a Gorilla 

    Crusher.Crush(c); 
    Crusher.Crush(g); 
} 

そしてヴィオラを実装します!あなたは車を粉砕して「クランチ」を得ることができる粉砕機を持っています。ゴリラを粉砕して、「Squish!」を得ることができます。 CarsとGorillasの間の型の関係や、コンパイル時の型チェック(実行時のswitch文の代わりに)を見つけるプロセスを経る必要はありません。

今、比較できるクラス(例えば、IComparable)です。クラスは、その型の2つのものをどのように比較するかを定義します。

コメント数:これで、ゴリラの配列を並べ替えることができます。まず、重みを使って並べ替えるものを追加します(重み付けしてゴリラを並べ替えるという不思議なビジネスロジックは無視してください)。ここでは関係ありません。その後、我々は、我々は多くの言語を持っていることをsingle inheritanceの制限を「周りの得」しているICompararble ...

public class Gorilla : ICrushable, IComparable 
{ 
    public int Weight 
    { 
     get; 
     set; 
    } 

    public void MakeCrushingSound() 
    { 
     Console.WriteLine("Squish!!"); 
    } 

    public int CompareTo(object obj) 
    { 
     if (!(obj is Gorilla)) 
     { 
      throw (new ArgumentException()); 
     } 

     var lhs = this; 
     var rhs = obj as Gorilla; 

     return (lhs.Weight.CompareTo(rhs.Weight)); 
    } 

} 

お知らせを実装します。私たちは好きなだけ多くのインターフェースを実装することができます。さて、これを行うだけで、私たちは今作成したばかりのクラスで10年以上前に書かれた機能を使用することができます(Array.SortArray.BinarySearch)。我々は今、次のコードを記述することができます...

var gorillas = new Gorilla[] { new Gorilla() { Weight = 900 }, 
           new Gorilla() { Weight = 800 }, 
           new Gorilla() { Weight = 850 } 
}; 

Array.Sort(gorillas); 
var res = Array.BinarySearch(gorillas, 
    new Gorilla() { Weight = 850 }); 

私のゴリラは、ソート、バイナリ検索を使用すると、インタフェースを記述するために850

+0

最後の割り当てにIComparableを使用したことを覚えています。完全に間違っていない場合は、このインターフェイスを実装していないオブジェクトを比較できないことを覚えておいてください。 –

0

誰軍の重量とのマッチングゴリラを見つけますし、何の言語ではありませんそれを強制する。良いプログラマーが従うということは、そのベストプラクティスとイディオムです。あなたはあなたのコードを使用する唯一の人です。そして、あなたが好きなものを書くことができますが、プロジェクトを離れ、他の誰かがそれを維持したり延長しなければならない場合はどうなりますか?あるいは、あなたのコードの使用を検討している他のプロジェクトがあればどうしますか?または、しばらくしてから、機能を追加したりリファクタリングを行うためにコードを再訪しなければならない場合はどうでしょうか?あなたはこれらの種類のもののために悪夢を作ります。あなたのオブジェクトの関係と契約が確立したことを理解するのは難しいでしょう。

+0

それはなぜ私が将来的に私や他の人に影響を与える恐ろしい間違いをするのを避けるために、なぜそれらを使用することが良いのか理解しようとしている。私は知識を構築しようとしています。 –

+0

右。私は可能性のある悪影響、より少ないメンテナンス性、より多くの依存性、きれいなコードを表現しました。契約を守っている限り、それらを実装する方法はすべてフリースタイルであり、多形性には優れているため、コード化することも良いはずです。 – jimx

0

抽象度: インタフェースを使用するように記述されたコードは、再利用可能であり、変更する必要はありません。下の例では、System.Array、System.ArrayList、System.Collection.CollectionBase、List of Tは、すべてIListを実装しているため、このサブはSystem.Array、System.ArrayList、System.Collection.CollectionBaseで動作します。既存のクラスは、クラスが別のクラスを継承しても簡単にインタフェースを実装できます。 サブジェクトでIListを実装するためのクラスを作成することもできます。あるいは、別のプログラムが、サブプログラムで使用するインタフェースを実装することもできます。

公共サブのdoSomething(IListのとしてByVal値) エンドサブ

あなたはまた、そうクラスはIListのとIEnumerableを、ほとんどの言語であなたが1つのクラスを継承することができます両方にすることができ、クラスに複数のインタフェースを使用することができます。

さまざまなフレームワークでどのように使用されているかを見ていきます。

0

私はあなたの質問を理解しているので、なぜインタフェースが必要なのでしょうか?右 ?まあ、我々は彼らを 必要はありません:)

C++では、たとえば、あなたがどのように見えるのテンプレート...と言うダミー関数を定義するとき::

template <typename T> 
void fun(const T& anObjectOfAnyType) 
{ 
    anyThing.anyFunction(); 
} 

あなたがどこでも、この機能を使用することができますanyFunction ... という関数があるタイプは、コンパイラが行う唯一のことは、タイプTの名前をタイプ に置き換え、新しいコードをコンパイルすることです。

これは実際にはエラーが起こりやすいです。その理由は、anyFunctionを持たない型をプラグインするとエラーが発生し、そのエラーは毎回異なります。 コンパイラによって変換できないすべての行でエラーが発生します。唯一の欠けているもののための多くのエラーを得る! 新しいタイプには、われわれの楽しいところで正しく動作するために必要な機能がありません。

ここでインターフェイスはこの問題全体を解決します。 型に必要な関数がある場合はそれが適切です。そうでない場合、コンパイラは型が適切でないというエラーを発行します。

テンプレートの例は説明のためのもので、javaがインタフェースを持たない場合に起こることをイメージングする場合は、すべてのクラスのすべての関数が手動で存在するかどうかを確認する必要があります。そのクラスが特定の関数を実装していると仮定します。汚い仕事は、コンパイラによって行われます:)

おかげで、

関連する問題