Observer パターン

出典: フリー百科事典『ウィキペディア(Wikipedia)』
ナビゲーションに移動 検索に移動

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

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

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

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

クラス図[編集]

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

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

Observer-pattern-class-diagram.png

各クラスの解説[編集]

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

Subject[編集]

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

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

  • addObserver() - Subjectが持つ「通知を受け取るObserver群」に、新たなObserverを加える。
  • removeObserver() - addObserver()で追加されたオブジェクトを削除する。
  • 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 風文法で Observer パターンを記述したものである。

class Listener:
    def __init__(self, name, subject):
        self.name = name
        subject.register(self)

    def update(self, event):
        print self.name, "received event", event

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

    def register(self, listener):
        self.listeners.append(listener)

    def unregister(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)
listenerB = Listener("<listener B>", subject)
# subject には2つのリスナーが登録されている。
subject.notify_listeners("<event 1>")
# 出力:
#     <listener A> received event <event 1>
#     <listener B> received event <event 1>

Java[編集]

ロックを避けるため、CopyOnWriteArraySetを使用する。

// Listener.java
public interface Listener {
    public void onEvent();
}
// Subject.java
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.Set;

public class Subject {
    private Set<Listener> listenerSet = new CopyOnWriteArraySet<Listener>();

    public void addListener(Listener listener) {
        listenerSet.add(listener);
    }

    public void removeListener(Listener listener) {
        listenerSet.remove(listener);
    }

    private void notifyListeners() {
        for (Listener listener : listenerSet) {
            listener.onEvent();
        }
    }
}

実装[編集]

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

脚注[編集]

関連項目[編集]

外部リンク[編集]