Observer パターン

en パターンとは、プログラム内のオブジェクトに関するイベントを他のオブジェクトへ通知する処理で使われるデザインパターンの一種。

通知するオブジェクト側が、通知されるオブジェクト側に観測・観察される形になることから、こう呼ばれる。

出版-購読型モデルとも呼ばれる。暗黙的呼び出しの原則と関係が深い。

分散イベント処理システムの実装にも使われる。言語によっては、このパターンで扱われる問題は言語が持つイベント処理構文で処理される。

1. クラス図
-------

このパターンの基本は、イベントを通知される側の1つ以上のオブジェクトを、通知する側のオブジェクトに登録することである。そして通知に使われるメソッドが、抽象メソッドになっていることが重要である。言語によっては、コールバック関数と通知対象コンテキストのペア、あるいはそれらをカプセル化した関数オブジェクト、またはデリゲートが使われる。

以下に、その構造をUMLクラス図で視覚化したものを示す。

2. 各クラスの解説
----------

このパターンに登場する各インタフェースとインタフェースの実装クラスを、以下で解説する。

2.1. Subject
------------

イベントを通知するオブジェクト側のインタフェース。1つ以上のObserverすなわちイベントを通知されるオブジェクト側のインタフェースの登録・削除・通知のメソッド書式の体裁を提供する。

以下の抽象メソッドを持つ:

//addObserver// - Subjectが持つ「通知を受け取るObserver群」に、新たなObserverを加える「登録する」動作を意味するregisterという名前が使われることもある。}}。
//removeObserver// - addObserverで追加されたオブジェクトを削除する「登録解除する」動作を意味するunregisterという名前が使われることもある。}}。
//notifyObservers// - Subjectが持つ「通知を受け取るObserver群」に、Observer.notify を呼んでイベントを通知する。上のUMLクラス図では、Subjectがインタフェースと実装クラスに分かれているが、パターン要件ではない。インタフェースを使わずクラスを直接実装することもある。

2.2. ConcreteSubject
--------------------

Subjectの実装クラス。通知対象であるObserver群を持つ。各Observerが受け取る通知に関する処理を司る。notifyObservers を呼ぶと、Observer群の1つ以上にイベントを通知する。

2.3. Observer
-------------

イベントを通知される側のインタフェース。以下の抽象メソッドを持つ:

//notify// - Observerにとっては通知を受け取る処理、このメソッドを呼ぶSubjectにとっては通知を送る処理、と言える。このメソッドの個数や各書式は、通知内容により様々である。

2.4. ConcreteObserver
---------------------

Observerの実装クラス。

3. 典型的用法
--------

ユーザーが何らかの操作をするなどの外部イベントを待つ。イベント駆動型プログラミングを参照。
あるオブジェクトの属性値の変化を待つ。なお、複数の属性値の変化でコールバック関数を呼び出すようにしているとイベントの連鎖的発生を引き起こす。
メーリングリストで、何らかのイベントがあったとき、購読者リストに登録している人にメッセージを送る。Observer パターンは Model View Controller  パラダイムの実装に使われることも多い。MVC では、モデルとビューの連携に Observer パターンが使われる。通常、コントローラーがモデルの変化を検出し、ビューに通知する。

4. コード例
-------

4.1. Python
-----------

以下のコードは Python 3.x で Observer パターンを記述したものである。引数を1つ受け取るupdateメソッドを持つオブジェクトであれば、リスナーとして何でも受け付ける。なお、例ではリスナーの集合を保持するためにリストを使用しているため、同じオブジェクトの多重登録を許可する実装となっている。

class Listener: def __init__: self.name = name

def update: print

class Subject: def __init__: self.listeners = []

def add_listener: self.listeners.append

def remove_listener: self.listeners.remove

def notify_listeners: for listener in self.listeners: listener.update

subject = Subject listenerA = Listener subject.add_listener listenerB = Listener subject.add_listener

subject には2つのリスナーが登録されている。subject.notify_listeners<WtXmlEndTag />

出力: received event received event

4.2. Java
---------

ロックを避けるため、java/util/concurrentを使用する例を示す。スレッドセーフにする必要がない、あるいはsynchronizedで同期するのであれば、java/utilやjava/utilを使ってもかまわないが、コンテナの実装によっては順序が保証されず、リスナーを追加したときの順番でupdateが呼ばれるとは限らない。

// Listener.java

public interface Listener { public void update; }<WtXmlEndTag />

// Subject.java

import java.util.concurrent.CopyOnWriteArraySet; import java.util.Set;

public class Subject { private final Set listenerSet = new CopyOnWriteArraySet;

public void addListener { listenerSet.add; }

public void removeListener { listenerSet.remove; }

public void notifyListeners { for  { listener.update; } } }<WtXmlEndTag />

// Main.java

class ListenerImpl implements Listener { private final String name;

public ListenerImpl { this.name = name; }

@Override public void update { System.out.println; } }

public class Main { public static void main throws Exception { Subject subject = new Subject; Listener listenerA = new ListenerImpl; subject.addListener; Listener listenerB = new ListenerImpl; subject.addListener; subject.notifyListeners; } }<WtXmlEndTag />

出力結果はPythonの例と同じである。

5. 実装
-----

Observer パターンは各種ライブラリやシステムに実装されている。特にGUIツールキットには必ず含まれる。

Java標準クラスライブラリにはname=java.util.Observerインタフェースとname=java.util.Observableクラスが用意されていたが、ともにJava 9で非推奨となった。
Java Swing ライブラリは Observer パターンを多用している。
[1] - Boost C++ライブラリ。signal/slot モデルを提供する。Boost 1.54以降では非推奨。
[2] - Boost.Signalsの後継。
[3] - C++用アプリケーションフレームワークのQtでは、signal/slot モデルを採用している。
[4] - C++ シグナルプログラミング・テンプレートライブラリ
[5] - C++ Signal/Slot ライブラリ
[6] - テンプレートベースの C++ signal/slot モデル
GLib - C言語でのオブジェクトと signals/callbacks の実装
[7] - C#とVisual Basic .NETによる実装例。デリゲートとevent構文を利用。
[8] - REALbasic による実装
[9] - ActionScript 3.0 でのパッケージ
[10] - カスタムイベントを Observer パターンで実装
[11] - Python 実装

6. 脚注
-----

6.1. 注釈
-------

6.2. 出典
-------

7. 関連項目
-------

デザインパターン 
デリゲート 

8. 外部リンク
--------

[12]
[13]
[14]
[15]
[16]
[17]