Observer パターン
この記事内にあるすべての画像は、ベクターイメージである SVG ファイルとして再作成されるべきです。これにはいくつかの利点があります。詳しくはWikipedia:SVGへの乗り換えを参照してください。この画像の SVG 形式がすでに利用可能である場合は、アップロードしてください。アップロード後、この画像にあるこのテンプレートを{{SVG版利用可能|新しい画像ファイル名.svg}}テンプレートと置き換えてください。 |
Observer パターン(オブザーバー・パターン)とは、プログラム内のオブジェクトに関するイベント(事象)を他のオブジェクトへ通知する処理で使われるデザインパターンの一種。
通知するオブジェクト側が、通知されるオブジェクト側に観測・観察(英: observe)される形になることから、こう呼ばれる。
出版-購読型モデルとも呼ばれる。暗黙的呼び出しの原則と関係が深い。
分散イベント処理システムの実装にも使われる。言語によっては、このパターンで扱われる問題は言語が持つイベント処理構文で処理される。
クラス図
[編集]このパターンの基本は、イベントを通知される側の1つ以上のオブジェクト(オブザーバーまたはリスナーと呼ぶ)を、通知する側のオブジェクト(サブジェクトと呼ぶ)に登録することである。そして通知に使われるメソッドが、抽象メソッドになっていることが重要である。言語によっては、コールバック関数と通知対象コンテキストのペア、あるいはそれらをカプセル化した関数オブジェクト、またはデリゲートが使われる[1]。
以下に、その構造をUMLクラス図で視覚化したものを示す。
各クラスの解説
[編集]このパターンに登場する各インタフェースとインタフェースの実装クラスを、以下で解説する。
Subject
[編集]イベントを通知するオブジェクト側のインタフェース。1つ以上のObserverすなわちイベントを通知されるオブジェクト側のインタフェースの登録・削除・通知のメソッド書式の体裁を提供する。
以下の抽象メソッドを持つ:
- addObserver() - Subjectが持つ「通知を受け取るObserver群」に、新たなObserverを加える[注釈 1]。
- removeObserver() - addObserver()で追加されたオブジェクトを削除する[注釈 2]。
- notifyObservers() - Subjectが持つ「通知を受け取るObserver群」に、Observer.notify() を呼んでイベントを通知する。
上のUMLクラス図では、Subjectがインタフェースと実装クラスに分かれているが、パターン要件ではない。インタフェースを使わずクラスを直接実装することもある。
ConcreteSubject
[編集]Subjectの実装クラス。通知対象であるObserver群を持つ。各Observerが受け取る通知に関する処理を司る。notifyObservers() を呼ぶと、Observer群の1つ以上にイベントを通知する。
Observer
[編集]イベントを通知される側のインタフェース。以下の抽象メソッドを持つ:
- notify() - Observerにとっては通知を受け取る処理、このメソッドを呼ぶSubjectにとっては通知を送る処理、と言える。このメソッドの個数や各書式は、通知内容により様々である。
ConcreteObserver
[編集]Observerの実装クラス。
典型的用法
[編集]- ユーザーが何らかの操作をするなどの外部イベントを待つ。イベント駆動型プログラミングを参照。
- あるオブジェクトの属性値の変化を待つ。なお、複数の属性値の変化でコールバック関数を呼び出すようにしているとイベントの連鎖的発生を引き起こす。
- メーリングリストで、何らかのイベント(新製品情報など)があったとき、購読者リストに登録している人にメッセージを送る。
Observer パターンは Model View Controller (MVC) パラダイムの実装に使われることも多い。MVC では、モデルとビューの連携に Observer パターンが使われる。通常、コントローラーがモデルの変化を検出し、ビュー(オブザーバー)に通知する。
コード例
[編集]Python
[編集]以下のコードは Python 3.x で Observer パターンを記述したものである。引数を1つ受け取るupdate()
メソッドを持つオブジェクトであれば、リスナーとして何でも受け付ける(ダック・タイピング)。なお、例ではリスナーの集合を保持するためにリストを使用しているため、同じオブジェクトの多重登録を許可する実装となっている。
class Listener:
def __init__(self, name):
self.name = name
def update(self, event):
print(self.name, "received event", event)
class Subject:
def __init__(self):
self.listeners = []
def add_listener(self, listener):
self.listeners.append(listener)
def remove_listener(self, listener):
self.listeners.remove(listener)
def notify_listeners(self, event):
for listener in self.listeners:
listener.update(event)
subject = Subject()
listenerA = Listener("<listener A>")
subject.add_listener(listenerA)
listenerB = Listener("<listener B>")
subject.add_listener(listenerB)
# subject には2つのリスナーが登録されている。
subject.notify_listeners("<event 1>")
出力:
<listener A> received event <event 1>
<listener B> received event <event 1>
Java
[編集]ロックを避けるため、CopyOnWriteArraySet
を使用する例を示す。スレッドセーフにする必要がない、あるいはsynchronized
で同期するのであれば、HashSet
やTreeSet
を使ってもかまわないが、コンテナの実装によっては順序が保証されず、リスナーを追加したときの順番でupdate()
が呼ばれるとは限らない。
// Listener.java
public interface Listener {
public void update(String event);
}
// Subject.java
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.Set;
public class Subject {
private final Set<Listener> listenerSet = new CopyOnWriteArraySet<Listener>();
public void addListener(Listener listener) {
listenerSet.add(listener);
}
public void removeListener(Listener listener) {
listenerSet.remove(listener);
}
public void notifyListeners(String event) {
for (Listener listener : listenerSet) {
listener.update(event);
}
}
}
// Main.java
class ListenerImpl implements Listener {
private final String name;
public ListenerImpl(String name) {
this.name = name;
}
@Override
public void update(String event) {
System.out.println(this.name + " received event " + event);
}
}
public class Main {
public static void main(String[] args) throws Exception {
Subject subject = new Subject();
Listener listenerA = new ListenerImpl("<listener A>");
subject.addListener(listenerA);
Listener listenerB = new ListenerImpl("<listener B>");
subject.addListener(listenerB);
subject.notifyListeners("<event 1>");
}
}
出力結果はPythonの例と同じである。
実装
[編集]この節に雑多な内容が羅列されています。 |
ウィキペディアはオンライン百科事典であって、情報を無差別に収集する場ではありません。 |
Observer パターンは各種ライブラリやシステムに実装されている。特にGUIツールキットには必ず含まれる。
- Java標準クラスライブラリには
java.util.Observer
インタフェースとjava.util.Observable
クラスが用意されていたが、ともにJava 9で非推奨となった[2]。 - Java Swing ライブラリは Observer パターンを多用している。
- Boost.Signals - Boost C++ライブラリ。signal/slot モデルを提供する。Boost 1.54以降では非推奨。
- Boost.Signals2 - Boost.Signalsの後継。
- Signals & Slots | Qt Core 5 - C++用アプリケーションフレームワークのQtでは、signal/slot モデルを採用している。
- libsigc++ - C++ シグナルプログラミング・テンプレートライブラリ
- sigslot - C++ Signal/Slot ライブラリ
- XLObject - テンプレートベースの C++ signal/slot モデル
- GLib - C言語でのオブジェクトと signals/callbacks の実装(他のプログラミング言語用の実装もある)
- Exploring the Observer Design Pattern | Microsoft Docs - C#とVisual Basic .NETによる実装例。デリゲートとevent構文を利用。
- Using the Observer Pattern - REALbasic による実装
- flash.events - ActionScript 3.0 でのパッケージ(ActionScript 2.0 の mx.events パッケージの後継)
- YUI Event utility - カスタムイベントを Observer パターンで実装
- Py-notify - Python 実装
脚注
[編集]注釈
[編集]出典
[編集]関連項目
[編集]外部リンク
[編集]この節に雑多な内容が羅列されています。 |