インターフェイスは、一緒に動作する必要があるが、可能な限り互いに分離する必要がある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();
}
}
動作の順序は次のようになります。
- アプリケーションが起動し、ユーザーがログインする彼女は
UserProfileWindow
彼女を開きます。
UserProfileWindow
は、それ自身をUserListener
として追加します。
- ユーザーはアイドル状態になり、15分間キーボードまたはマウスに触れません。
InactivityTimer
クラス通知とUser.logOut
を呼び出します。
User.logOut
はモデルを更新し、currentUser
変数をクリアします。もし誰かが尋ねれば、誰もログインしていません。
User.logOut
リスナーリストでループし、各リスナーでloggedOut()
を呼び出します。
UserProfileWindow
のloggedOut()
メソッドが呼び出され、ウィンドウが閉じます。
このUser
クラスは、ログアウトイベントについて誰が知っておく必要があるかについて絶対に何も知らないので、これは素晴らしいことです。ユーザー名ラベルを更新する必要があること、プロファイルウィンドウを閉じる必要があることなどは知られていません。後で、ユーザーがログアウトしたときにもっと多くのことを行う必要があると判断した場合は、User
クラスをまったく変更する必要はありません。
リスナーパターンは、インターフェイスが非常に便利な場合の1つの例です。インターフェイスはすべてデカップリングクラスに関するもので、互いにやりとりする必要のあるクラス間の結びつきや依存関係を取り除きますが、コード間に強い結びつきがあってはいけません。
良い例ですが、Firearmという抽象基本クラスについても同じことが言えます。 –
もちろん、インタフェースは抽象基本クラスで定義することができます。これは、インターフェイスのための別の構文を持たないC++でインターフェイスを定義する方法です。 – dewtell