コンテンツにスキップ
Wikipedia

Observer パターン

出典: フリー百科事典『ウィキペディア(Wikipedia)』
この記事には複数の問題があります改善ノートページでの議論にご協力ください。
この記事内にあるすべての画像は、ベクターイメージである 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
publicinterface Listener{
publicvoidupdate(Stringevent);
}
// Subject.java
importjava.util.concurrent.CopyOnWriteArraySet;
importjava.util.Set;
publicclass Subject{
privatefinalSet<Listener>listenerSet=newCopyOnWriteArraySet<Listener>();
publicvoidaddListener(Listenerlistener){
listenerSet.add(listener);
}
publicvoidremoveListener(Listenerlistener){
listenerSet.remove(listener);
}
publicvoidnotifyListeners(Stringevent){
for(Listenerlistener:listenerSet){
listener.update(event);
}
}
}
// Main.java
class ListenerImplimplementsListener{
privatefinalStringname;
publicListenerImpl(Stringname){
this.name=name;
}
@Override
publicvoidupdate(Stringevent){
System.out.println(this.name+" received event "+event);
}
}
publicclass Main{
publicstaticvoidmain(String[]args)throwsException{
Subjectsubject=newSubject();
ListenerlistenerA=newListenerImpl("<listener A>");
subject.addListener(listenerA);
ListenerlistenerB=newListenerImpl("<listener B>");
subject.addListener(listenerB);
subject.notifyListeners("<event 1>");
}
}

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

実装

[編集 ]
この節に雑多な内容が羅列されています 事項を箇条書きで列挙しただけの節は、本文として組み入れるか、または整理・除去する必要があります。(2021年8月)
ウィキペディアはオンライン百科事典であって、情報を無差別に収集する場ではありません 改善ノートページでの議論にご協力ください。(2021年8月)

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

脚注

[編集 ]

注釈

[編集 ]
  1. ^ 「登録する」動作を意味するregister()という名前が使われることもある。
  2. ^ 「登録解除する」動作を意味するunregister()という名前が使われることもある。

出典

[編集 ]

関連項目

[編集 ]

外部リンク

[編集 ]
この節に雑多な内容が羅列されています 事項を箇条書きで列挙しただけの節は、本文として組み入れるか、または整理・除去する必要があります。(2021年8月)
出典は列挙するだけでなく、脚注などを用いてどの記述の情報源であるかを明記してください。 記事の信頼性向上にご協力をお願いいたします。(2021年8月)
GoFによる23種のパターン
生成に関するパターン
構造に関するパターン
振る舞いに関するパターン
並行性に関するパターン
アーキテクチャに関するパターン
その他のパターン
関連する人々
関連項目

AltStyle によって変換されたページ (->オリジナル) /