Observer パターン

Observerパターンとは



Observerパターンは、オブジェクトの状態変化を他のオブジェクトに通知する際に用いられるデザインパターンの一つです。これは、イベント駆動型プログラミングや、Model-View-Controller(MVC)アーキテクチャなどで広く活用されており、システムを疎結合に保ちながら、効率的なデータ伝播を実現するのに役立ちます。

基本構造



Observerパターンの基本は、以下の要素で構成されます。

Subject(サブジェクト): イベントを通知するオブジェクト。オブザーバーを登録、削除、通知するインターフェースを提供します。
ConcreteSubject(具象サブジェクト): Subjectインターフェースを実装したクラス。実際にオブザーバーへの通知処理を行います。
Observer(オブザーバー): イベントの通知を受け取るオブジェクトのインターフェース。
ConcreteObserver(具象オブザーバー): Observerインターフェースを実装したクラス。通知を受け取った際の具体的な処理を定義します。

UMLクラス図



Observerパターンの構造をUMLクラス図で表現すると、以下のようになります。

mermaid
classDiagram
class Subject {
<>
+addObserver(observer: Observer)
+removeObserver(observer: Observer)
+notifyObservers()
}
class ConcreteSubject {
-observers: List
+addObserver(observer: Observer)
+removeObserver(observer: Observer)
+notifyObservers()
}
class Observer {
<>
+notify()
}
class ConcreteObserver {
+notify()
}
Subject <|-- ConcreteSubject
Observer <|-- ConcreteObserver
ConcreteSubject -- Observer


各クラスの役割



Subject: オブザーバーの登録、削除、通知のインターフェースを提供します。
addObserver(): オブザーバーを登録します。
removeObserver(): オブザーバーを削除します。
notifyObservers(): 登録された全てのオブザーバーに対して通知を行います。
ConcreteSubject: Subjectインターフェースを実装し、実際にオブザーバーへの通知を管理します。
Observer: 通知を受け取る側のインターフェースです。`notify()`メソッドを持ち、サブジェクトからの通知を受け取ります。
ConcreteObserver: Observerインターフェースを実装し、通知を受け取った際の具体的な処理を記述します。

典型的な使用例



Observerパターンは、以下のようなシナリオで効果的に使用できます。

イベント駆動型プログラミング: ユーザーの操作やシステムイベントが発生した際に、関連するオブジェクトに通知を送る。
オブジェクトの属性値の変化: あるオブジェクトの状態が変化したときに、他のオブジェクトに自動的に通知する。
メーリングリスト: 新製品情報などのイベントが発生した際に、購読者に通知を送る。
Model View Controller (MVC): モデルの状態変化をビューに通知する。

コード例



以下にPythonとJavaでの実装例を示します。

Python


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

def attach(self, observer):
self._observers.append(observer)

def detach(self, observer):
self._observers.remove(observer)

def notify(self):
for observer in self._observers:
observer.update(self)

class ConcreteSubject(Subject):
def __init__(self):
super().__init__()
self._state = None

@property
def state(self):
return self._state

@state.setter
def state(self, value):
self._state = value
self.notify()

class Observer:
def update(self, subject):
pass

class ConcreteObserver(Observer):
def __init__(self, name):
self.name = name

def update(self, subject):
print(f'{self.name} received update: {subject.state}')


subject = ConcreteSubject()
observer1 = ConcreteObserver('Observer 1')
observer2 = ConcreteObserver('Observer 2')

subject.attach(observer1)
subject.attach(observer2)

subject.state = 'New State'


Java


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

interface Observer {
void update(Subject subject);
}

interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}

class ConcreteSubject implements Subject {
private Set observers = new CopyOnWriteArraySet<>();
private String state;

@Override
public void attach(Observer observer) {
observers.add(observer);
}

@Override
public void detach(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
notifyObservers();
}
}

class ConcreteObserver implements Observer {
private String name;

public ConcreteObserver(String name) {
this.name = name;
}

@Override
public void update(Subject subject) {
System.out.println(name + " received update: " + ((ConcreteSubject) subject).getState());
}
}

public class Main {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
ConcreteObserver observer2 = new ConcreteObserver("Observer 2");

subject.attach(observer1);
subject.attach(observer2);

subject.setState("New State");
}
}


実装例



Observerパターンは多くのライブラリやフレームワークで利用されています。

Java Swing
Boost.Signals (C++)
Qt (C++)
.NET (C# / Visual Basic)
ActionScript
YUI Event utility

まとめ



Observerパターンは、オブジェクト間の依存関係を疎結合にし、変更を柔軟に対応できるようにする強力なデザインパターンです。イベント駆動型システムやGUIアプリケーションなど、様々な場面で活用することができます。このパターンを理解し、適切に利用することで、より堅牢で保守性の高いソフトウェア開発が可能になります。

もう一度検索

【記事の利用について】

タイトルと記事文章は、記事のあるページにリンクを張っていただければ、無料で利用できます。
※画像は、利用できませんのでご注意ください。

【リンクついて】

リンクフリーです。