Observer パターン

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

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

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

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

 クラス図 
このパターンの基本は、イベントを通知される側の1つ以上のオブジェクト（オブザーバーまたはリスナーと呼ぶ）を、通知する側のオブジェクト（サブジェクトと呼ぶ）に登録することである。そして通知に使われるメソッドが、抽象メソッドになっていることが重要である。言語によっては、コールバック関数と通知対象コンテキストのペア、あるいはそれらをカプセル化した関数オブジェクト、またはデリゲートが使われるイベントのサブスクリプションとサブスクリプション解除を行う方法 - C# プログラミング ガイド | Microsoft Docs。

以下に、その構造を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 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 
ロックを避けるため、を使用する例を示す。スレッドセーフにする必要がない、あるいはsynchronizedで同期するのであれば、やを使ってもかまわないが、コンテナの実装によっては順序が保証されず、リスナーを追加したときの順番で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 9で非推奨となった[JDK-8154801] deprecate Observer and Observable - Java Bug System。
 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 実装

 脚注 
 注釈 

 出典 

 関連項目 
 デザインパターン (ソフトウェア)
 デリゲート (プログラミング)

 外部リンク 

 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

Category:デザインパターン (ソフトウェア)
Category:認識