Observer パターン

Observer パターン（オブザーバー・パターン）とは、プログラム内のオブジェクトに関するイベント（事象）を他のオブジェクトへ通知する処理で使われるデザインパターンの一種。 通知するオブジェクト側が、通知されるオブジェクト側に観測・観察（英: observe）される形になることから、こう呼ばれる。 出版-購読型モデルとも呼ばれる。暗黙的呼び出しの原則と関係が深い。 分散イベント処理システムの実装にも使われる。言語によっては、このパターンで扱われる問題は言語が持つイベント処理構文で処理される。 このパターンの基本は、イベントを通知される側の1つ以上のオブジェクト（オブザーバーまたはリスナーと呼ぶ）を、通知する側のオブジェクト（サブジェクトと呼ぶ）に登録することである。そして通知に使われるメソッドが、抽象メソッドになっていることが重要である。言語によっては、コールバック関数と通知対象コンテキストのペア、あるいはそれらをカプセル化した関数オブジェクト、またはデリゲートが使われる。 以下に、その構造をUMLクラス図で視覚化したものを示す。 このパターンに登場する各インタフェースとインタフェースの実装クラスを、以下で解説する。 イベントを通知するオブジェクト側のインタフェース。1つ以上のObserverすなわちイベントを通知されるオブジェクト側のインタフェースの登録・削除・通知のメソッド書式の体裁を提供する。 以下の抽象メソッドを持つ: addObserver() - Subjectが持つ「通知を受け取るObserver群」に、新たなObserverを加える。 removeObserver() - addObserver()で追加されたオブジェクトを削除する。 notifyObservers() - Subjectが持つ「通知を受け取るObserver群」に、Observer.notify() を呼んでイベントを通知する。 上のUMLクラス図では、Subjectがインタフェースと実装クラスに分かれているが、パターン要件ではない。インタフェースを使わずクラスを直接実装することもある。 Subjectの実装クラス。通知対象であるObserver群を持つ。各Observerが受け取る通知に関する処理を司る。notifyObservers() を呼ぶと、Observer群の1つ以上にイベントを通知する。 イベントを通知される側のインタフェース。以下の抽象メソッドを持つ: notify() - Observerにとっては通知を受け取る処理、このメソッドを呼ぶSubjectにとっては通知を送る処理、と言える。このメソッドの個数や各書式は、通知内容により様々である。 Observerの実装クラス。 ユーザーが何らかの操作をするなどの外部イベントを待つ。イベント駆動型プログラミングを参照。 あるオブジェクトの属性値の変化を待つ。なお、複数の属性値の変化でコールバック関数を呼び出すようにしているとイベントの連鎖的発生を引き起こす。 メーリングリストで、何らかのイベント（新製品情報など）があったとき、購読者リストに登録している人にメッセージを送る。 Observer パターンは Model View Controller (MVC) パラダイムの実装に使われることも多い。MVC では、モデルとビューの連携に Observer パターンが使われる。通常、コントローラーがモデルの変化を検出し、ビュー（オブザーバー）に通知する。 以下のコードは 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> ロックを避けるため、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で非推奨となった。 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 実装 ^ 「登録する」動作を意味するregister()という名前が使われることもある。 ^ 「登録解除する」動作を意味するunregister()という名前が使われることもある。 ^ イベントのサブスクリプションとサブスクリプション解除を行う方法 - C# プログラミング ガイド | Microsoft Docs ^ [JDK-8154801] deprecate Observer and Observable - Java Bug System デザインパターン (ソフトウェア) デリゲート (プログラミング) A sample implementation in .NET Observer Pattern in Java Observer Pattern implementation in JDK 1.4 Using the Observer Pattern in .NET Definition & UML diagram A discussion of fine-grained observers