ソフトウェア設計およびソフトウェアエンジニアリングにおいて、オブザーバーパターンとは、サブジェクト(イベントソースまたはイベントストリームとも呼ばれる)と呼ばれるオブジェクトが、オブザーバー(イベントシンクとも呼ばれる)と呼ばれる依存オブジェクトのリストを保持し、状態の変化を自動的に通知するソフトウェア設計パターンです。通知は通常、オブザーバーのメソッドを呼び出すことによって行われます。サブジェクトは標準化されたインターフェースを通じてオブザーバーを認識し、購読リストを直接管理します。
このパターンでは、複数のオブザーバーが単一のサブジェクトを監視できる一対多の依存関係が構築されますが、結合は通常同期かつ直接的です。つまり、サブジェクトは変更が発生した際にオブザーバーのメソッドを呼び出しますが、イベントキューを使用した非同期実装も可能です。パブリッシュ/サブスクライブパターンとは異なり、仲介者は存在せず、サブジェクトとオブザーバーは互いに直接参照を持ちます。
これは、イベント駆動型プログラミング、特にGUIツールキットやMVCフレームワークなどのプロセス内システムにおいて、イベント処理システムを実装するためによく用いられます。そのため、ユーザー入力、HTTPリクエスト、GPIO信号、分散データベースからの更新、 GUIモデルの変更など、予測不可能なタイミングで到着するデータの処理に非常に適しています。
オブザーバーデザインパターンは、柔軟で再利用可能なオブジェクト指向ソフトウェアを設計するために繰り返し発生する設計上の課題に対処する、23の有名な「Gang of Four」デザインパターンの1つである振る舞いパターンであり、実装、変更、テスト、再利用が容易なオブジェクトを生み出します。[ 1 ]
オブザーバーパターンは、以下の要件に対応します。[ 2 ]
単純なアプローチとしては、1つのオブジェクト(主体)が各依存オブジェクトの特定のメソッドを直接呼び出すという方法があります。しかし、主体はすべての依存オブジェクトの具体的な型とインターフェースを把握する必要があるため、密結合が生じ、コードの柔軟性が損なわれ、拡張が困難になります。ただし、抽象化のオーバーヘッドが許容できず、コンパイル時の最適化が不可欠な、パフォーマンスが重視されるシナリオ(低レベルのカーネル構造やリアルタイムシステムなど)では、この直接的なアプローチが望ましい場合があります。
オブザーバーパターンは、標準的な通知プロトコルを確立することで、より柔軟な代替手段を提供する。
Subject。Observerupdate()操作を呼び出すことで状態の変化を通知する。このアプローチでは、インターフェースの標準化を通じて、主体とオブザーバーの結合を緩やかにします。主体は、オブザーバーがメソッドを実装していることだけを知っていればよくupdate()、オブザーバーの具体的な型や内部実装の詳細を知る必要はありません。オブザーバーは実行時に独立して追加および削除できます。
オブザーバーパターンとパブリッシュ/サブスクライブパターンは、どちらもコンポーネント間の1対多の通信をサポートするため、密接に関連しており、しばしば混同されます。しかし、アーキテクチャ、結合度、および一般的なユースケースにおいて、両者は大きく異なります。
以下の表は、主な相違点をまとめたものです。
実際には、パブリッシュ/サブスクライブシステムは、オブザーバーパターンのいくつかの制約に対処するために進化しました。典型的なオブザーバー実装では、主体とそのオブザーバーの間に密結合が生じます。これは、特に分散環境において、スケーラビリティ、柔軟性、保守性を制限する可能性があります。主体とオブザーバーは共通のインターフェースに準拠する必要があり、両者は互いの存在を認識しています。
この結合度を低減するために、パブリッシュ/サブスクライブシステムでは、パブリッシャーとサブスクライバーの間を取り持つメッセージブローカーまたはイベントバスが導入されます。この追加レイヤーにより、直接参照が不要になり、システムが独立して進化できるようになります。ブローカーは、メッセージの永続性、配信保証、トピックベースのフィルタリング、非同期通信などの機能もサポートする場合があります。
システムによっては、オブザーバーパターンが内部的に使用され、パブリッシュ/サブスクライブインターフェースの背後でサブスクリプションメカニズムを実装します。その他の場合、パターンは独立して適用されます。たとえば、JavaScriptライブラリやフレームワークは、オブザーバーのようなサブスクリプション(コールバック登録など)と、分離されたパブリッシュ/サブスクライブメカニズム(イベントエミッターやシグナルなど)の両方を提供することがよくあります。[ 3 ] [ 4 ]
歴史的に、 OS/2やMicrosoft Windowsのような初期のグラフィカルオペレーティングシステムでは、「publish-subscribe」や「event-driven programming」という用語は、オブザーバーパターンの同義語としてよく使われていました。[ 5 ]
デザインパターン[ 1 ]で形式化されたオブザーバーパターンは、購読解除、通知フィルタリング、配信保証、メッセージログなどの懸念事項を意図的に省略しています。これらの高度な機能は通常、堅牢なメッセージキューイングシステムで実装され、オブザーバーパターンは基礎的なメカニズムとして機能しますが、それだけでは十分ではありません。
オブザーバーパターンの一般的な欠点は、メモリリークが発生する可能性があることです。これは「リスナーの失効問題」として知られています。これは、サブジェクトがオブザーバーへの強い参照を保持している場合に発生し、オブザーバーが他の場所で不要になった場合でもガベージコレクションの対象になりません。このパターンでは通常、明示的な登録と登録解除の両方が必要となるため( disposeパターンと同様)、オブザーバーの登録解除を忘れると、ダングリング参照が残ってしまう可能性があります。この問題は、オブザーバー参照に弱い参照を使用することで軽減できます。これにより、ガベージコレクタが不要になったオブザーバーオブジェクトを回収できるようになります。
アプリケーションによっては、特にユーザーインターフェースにおいて、対象の状態が頻繁に変化するため、変更のたびに観察者に通知することは非効率的または逆効果となる場合があります。例えば、データモデルのわずかな変更ごとに再レンダリングされるビューは、応答しなくなったり、ちらついたりする可能性があります。
このような場合、タイマーなどのスロットリング機構を導入することで、オブザーバーパターンを変更し、通知を時間的に分離することができます。オブザーバーは状態が変化するたびに更新するのではなく、対象をポーリングするか、一定間隔で通知を受け取ることで、モデルの近似的かつ安定した状態を把握できます。
この手法は、プログレスバーなど、基となるプロセスが急速に変化する要素によく用いられます。小さな変化にいちいち反応するのではなく、オブザーバーは定期的に表示を更新することで、パフォーマンスとユーザビリティを向上させます。
この時間的分離の形式により、観察者は高頻度の更新に圧倒されることなく反応性を維持しつつ、対象の状態の全体的な傾向や進行状況を反映させることができる。

このUMLクラス図では、Subjectクラスは依存オブジェクトの状態を直接更新しません。代わりに、状態を更新するためのインターフェース ( )Subjectを参照します。これにより、依存オブジェクトの状態がどのように更新されるかに依存しなくなります。クラスとクラスは、自身の状態をサブジェクトの状態と同期させることで、インターフェースを実装します。Observerupdate()SubjectObserver1Observer2Observer
UMLシーケンス図は、実行時の相互作用を示しています。オブジェクトとオブジェクトは、自身を登録するためにを呼び出します 。の状態が変化すると仮定すると、は自身を呼び出します。は登録されたオブジェクトとオブジェクトを呼び出し、これらのオブジェクトはから変更されたデータ()を要求して状態を更新(同期)します。Observer1Observer2attach(this)Subject1Subject1Subject1notify()notify()update()Observer1Observer2getState()Subject1

ライブラリクラスは存在しますjava.util.Observerが、実装されたモデルが非常に限定的であったため、Java 9 で非推奨とjava.util.Observableなりました。
以下は、キーボード入力を受け取り、各入力行をイベントとして処理するJavaで記述された例です。 から文字列が提供されるとSystem.in、メソッドがnotifyObservers()呼び出され、すべてのオブザーバーにイベントの発生を通知します。通知方法は、各オブザーバーの更新メソッドを呼び出すことです。
パッケージorg.wikipedia.examples ;import java.util.ArrayList ; import java.util.List ; import java.util.Scanner ;interface Observer { void update ( String event ); } class EventSource { List < Observer > observers = new ArrayList <> (); public void notifyObservers ( String event ) { observers . forEach ( observer -> observer . update ( event )); } public void addObserver ( Observer observer ) { observers . add ( observer ); } public void scanSystemIn () { Scanner scanner = new Scanner ( System . in ); while ( scanner . hasNextLine ()) { String line = scanner . nextLine (); notifyObservers ( line ); } } }public class ObserverDemo { public static void main ( String [] args ) { System . out . println ( "テキストを入力してください: " ); EventSource eventSource = new EventSource (); eventSource . addObserver ( event -> System . out . printf ( "応答を受信しました: %s%n" , event ));eventSource.scanSystemIn ( ) ; } }C# はIObservable. [ 7 ]およびIObserver[ 8 ]インターフェイスと、デザインパターンを実装する方法に関するドキュメントを提供します。[ 9 ]
名前空間Wikipedia.Examples ;using System ; using System.Collections.Generic ;class Payload { internal string Message { get ; init ; } }class Subject : IObservable < Payload > { private readonly List < IObserver < Payload >> _observers = new ();IDisposable IObservable <Payload> .Subscribe ( IObserver <Payload> observer ) { if ( ! _observers.Contains ( observer ) ) { _observers.Add ( observer ) ; }return new Unsubscriber ( observer , _observers ); }internal void SendMessage ( string message ) { foreach ( IObserver <Payload> observer in _observers ) { observer.OnNext ( new Payload { Message = message } ) ; } } }internal class Unsubscriber : IDisposable { private readonly IObserver < Payload > _observer ; private readonly ICollection < IObserver < Payload >> _observers ;internal Unsubscriber ( IObserver < Payload > observer , ICollection < IObserver < Payload >> observers ) { _observer = observer ; _observers = observers ; }void IDisposable.Dispose ( ) { if ( _observer ! = null && _observers.Contains ( _observer ) ) { _observers.Remove ( _observer ) ; } } }internal class Observer : IObserver < Payload > { private string _message ;public void OnCompleted () { }public void OnError ( Exception error ) { }public void OnNext ( Payload value ) { _message = value.Message ; }internal IDisposable Register ( IObservable < Payload > subject ) { return subject . Subscribe ( this ); } }これはC++23による実装です。
import std ;using std :: reference_wrapper ; using std :: vector ;class Subject ; // Observerで使用するための前方宣言class Observer { private : //デストラクタでデタッチする Subject オブジェクトへの参照Subject & subject ; public : explicit Observer ( Subject & subj ) : subject { subj } { subject.attach ( * this ) ; }virtual ~ Observer () { subject . detach ( * this ); } Observer ( const Observer & ) = delete ; Observer & operator = ( const Observer & ) = delete ;virtual void update ( Subject & s ) const = 0 ; };// Subject はイベント生成の基底クラスですclass Subject { private : vector <refObserver> observers ; public : using RefObserver = reference_wrapper < const Observer > ; //接続されているすべてのオブザーバーに通知しますvoid notify () { for ( const Observer & x : observers ) { x.get ( ). update ( * this ) ; } } //オブザーバーを追加しますvoid attach ( const Observer & observer ) { observers.push_back ( observer ); } //オブザーバーを削除しますvoid detach ( Observer & observer ) { observers.remove_if ([ & observer ]( const RefObserver & obj ) -> bool { return & obj.get ( ) == & observer ; } ) ; } } ;// 使用例class ConcreteObserver : public Observer { public : explicit ConcreteObserver ( Subject & subj ) : Observer ( subj ) {} // 通知を取得するvoid update ( Subject & ) const override { std :: println ( "通知を受け取りました" ); } };int main ( int argc , char * argv []) { Subject cs ; ConcreteObserver co1 ( cs ) ; ConcreteObserver co2 ( cs ); cs.notify ( ); }プログラムの出力は以下のとおりです。
通知を受け取りました通知を受け取りましたclass EventSource { private observers = []private notifyObservers ( String event ) { observers.each { it ( event ) } }void addObserver ( observer ) { observers += observer }void scanSystemIn ( ) { var scanner = new Scanner ( System.in ) while ( scanner ) { var line = scanner.nextLine ( ) notifyObservers ( line ) } } }println 'テキストを入力してください: ' var eventSource = new EventSource ()eventSource.addObserver { event -> println "応答を受信しました: $ event " }eventSource.scanSystemIn ( )import java.util.Scannertypealias Observer = ( event : String ) -> Unit ;class EventSource { private var observers = mutableListOf < Observer > ()private fun notifyObservers ( event : String ) { observers.forEach { it ( event ) } }fun addObserver ( observer : Observer ) { observers += observer }fun scanSystemIn ( ) { val scanner = Scanner ( System.`in` ) while ( scanner.hasNext ( ) ) { val line = scanner.nextLine ( ) notifyObservers ( line ) } } }fun main ( arg : List < String > ) { println ( "テキストを入力してください: " ) val eventSource = EventSource ()eventSource.addObserver { event -> println ( "応答を受信しました: $ event " ) }eventSource.scanSystemIn ( ) }System.Generics.Collections 、System.SysUtilsを使用します。type IObserver = interface [ '{0C8F4C5D-1898-4F24-91DA-63F1DD66A692}' ] procedure Update ( const AValue : string ) ; end ;type TObserverManager = class private FObservers : TList < IObserver >; public constructor Create ; overload ; destructor Destroy ; override ; procedure NotifyObservers ( const AValue : string ) ; procedure AddObserver ( const AObserver : IObserver ) ; procedure UnregisterObserver ( const AObserver : IObserver ) ; end ;type TListener = class ( TInterfacedObject , IObserver ) private FName : string ; public constructor Create ( const AName : string ) ; reintroduce ; procedure Update ( const AValue : string ) ; end ;procedure TObserverManager.AddObserver ( const AObserver : IObserver ) ; begin if not FObservers.Contains ( AObserver ) then FObservers.Add ( AObserver ) ; end ;begin FreeAndNil ( FObservers ) ;継承; end ;procedure TObserverManager.NotifyObservers ( const AValue : string ) ; var i : Integer ; begin for i : = 0 to FObservers.Count - 1 do FObservers [ i ] .Update ( AValue ) ; end ;procedure TObserverManager.UnregisterObserver ( const AObserver : IObserver ) ; begin if FObservers.Contains ( AObserver ) then FObservers.Remove ( AObserver ) ; end ;constructor TListener . Create ( const AName : string ) ; begin inherited Create ; FName := AName ; end ;procedure TListener.Update ( const AValue : string ) ; begin WriteLn ( FName + 'リスナーが通知を受信しました: ' + AValue ) ; end ;procedure TMyForm . ObserverExampleButtonClick ( Sender : TObject ) ; var LDoorNotify : TObserverManager ; LListenerHusband : IObserver ; LListenerWife : IObserver ; begin LDoorNotify := TObserverManager . Create ; try LListenerHusband := TListener . Create ( 'Husband' ) ; LDoorNotify . AddObserver ( LListenerHusband ) ; LListenerWife := TListener . Create ( 'Wife' ) ; LDoorNotify . AddObserver ( LListenerWife ) ; LDoorNotify . NotifyObservers ( 'Someone is knocking on the door' ) ; finally FreeAndNil ( LDoorNotify ) ; end ; end ;出力
夫のリスナーに通知が届きました:誰かがドアをノックしています 妻のリスナーが通知を受け取った:誰かがドアをノックしている
Pythonでの同様の例:
from typing import AnyクラスSubject : _observers : list [ Observer ]def __init__ ( self ) -> None : self . _observers = []def register_observer ( self , observer : Observer ) - > None : self._observers.append ( observer )def notify_observers ( self , * args : tuple [ Any , ... ] , ** kwargs : dict [ str , Any ] ) - > None : for observer in self._observers : observer.notify ( self , * args , ** kwargs )class Observer : def __init__ ( self , subject : Subject ) -> None : subject . register_observer ( self )def notify ( self , subject : Subject , * args : tuple [ Any , ... ], ** kwargs : dict [ str , Any ]) -> None : print ( f "Got { args } , { kwargs } from { subject } " )subject : Subject = Subject () observer : Observer = Observer ( subject ) subject . notify_observers ( "test" , kw = "python" )# 出力: Got ('test',) {'kw': 'python'} From <__main__.Observable object at 0x0000019757826FD0>JavaScriptには、Object.observeオブザーバーパターンのより正確な実装であった非推奨の関数があります。[ 10 ]これは、監視対象オブジェクトが変更されるとイベントを発火します。非推奨のObject.observe関数がない場合、このパターンはより明示的なコードで実装できます。[ 11 ]
let Subject = { _state : 0 , _observers : [], add : function ( observer ) { this . _observers . push ( observer ); }, getState : function () { return this . _state ; }, setState : function ( value ) { this . _state = value ; for ( let i = 0 ; i < this . _observers . length ; i ++ ) { this . _observers [ i ]. signal ( this ); } } };let Observer = { signal : function ( subject ) { let currentValue = subject.getState ( ) ; console.log ( currentValue ) ; } }Subject.add ( Observer ) ; Subject.setState ( 10 ) ; // console.log に出力- 10