はじめに
GoFデザインパターンの一つで振る舞いに関するパターンである
Observerパターンについて見ていきたいと思います.
Observerパターンとは
Observerパターンは,状態の変更を通知する側のオブジェクト(Subject)と,
その変更を監視する側のオブジェクト(Observer)が存在します.
これにより,オブジェクト間の依存関係を
1 対多(1つのSubject 対 複数のObservers)に定義することができます.
また,オブジェクト同士の疎結合を保ちながら,
状態変化をリアルタイムに伝える仕組みを提供します.
そのため,イベント駆動型プログラムやGUIなどで使用されます.
Observerパターンの特徴
Observerパターンの基本構造は
監視対象:Subjectと観察者:Observerの関係性を定義します.
これにより,オブジェクト間の依存を最小限にして,
あるオブジェクトの状態変更をほかのオブジェクトに通知することができます.
Observerパターンの実装
Objectパターンを以下の構造を参考にして実装していきます.
これは,ClockがDigitalClockによって観察されており,時間が変更されるとDigitalClockがupdateメッセージを受け取ります.
その後,DigitalClockはClockに時間を尋ね,Clockから取得した時間を表示します.
まず,構造のそれぞれの定義について見ていきます.
Subject(観察対象)
- Observer(観察者)を登録・削除できるインタフェース
- 状態が変化したとき,登録されているすべてのObserverに通知
Observer(観察者)
- Subjectからの通知を受け取るためのインタフェース
- Subjectの状態に応じて自身の動作を更新
Clock(具体的な観察対象)
- Subjectの具体的な実装
- 自分自身の状態を管理し,状態の変化時に通知
DigitalClock(具体的な観察者)
- Observerの具体的な実装
- Subjectから通知を受け取り,自分の状態を更新
ソースコード
以下がそれぞれのソースコードと出力例になります.
import java.util.ArrayList;
import java.util.List;
public class Subject {
private final List<Observer> observers = new ArrayList<>();
// Observerを登録
public void register(Observer observer) {
observers.add(observer);
}
// Observerを解除
public void unregister(Observer observer) {
observers.remove(observer);
}
// 全Observerに通知
public void notifyObservers(int hours, int minutes, int seconds) {
for (Observer observer : observers) {
observer.update(hours, minutes, seconds);
}
}
}
public interface Observer {
void update(int hours, int minutes, int seconds);
}
class Clock {
private int hours;
private int minutes;
private int seconds;
private final List<Observer> observers = new ArrayList<>();
// Observerを登録
public void register(Observer observer) {
observers.add(observer);
}
// Observerを削除
public void unregister(Observer observer) {
observers.remove(observer);
}
// 状態が変わったときに通知
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(hours, minutes, seconds);
}
}
// 時間を設定し、通知を送信
public void setTime(int hours, int minutes, int seconds) {
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
notifyObservers();
}
}
public class DigitalClock implements Observer {
private final String name;
public DigitalClock(String name) {
this.name = name;
}
@Override
public void update(int hours, int minutes, int seconds) {
System.out.println(name + " - Current time: " + String.format("%02d:%02d:%02d", hours, minutes, seconds));
}
}
public class Main {
public static void main(String[] args) {
// Clockオブジェクト(観察対象)を作成
Clock clock = new Clock();
// DigitalClockオブジェクト(観察者)を作成して登録
DigitalClock digitalClock1 = new DigitalClock("DigitalClock1");
DigitalClock digitalClock2 = new DigitalClock("DigitalClock2");
clock.register(digitalClock1);
clock.register(digitalClock2);
// 時刻を設定して通知
clock.setTime(10, 30, 45);
clock.setTime(12, 15, 20);
// DigitalClock1を解除
clock.unregister(digitalClock1);
// 別の時刻を設定して通知
clock.setTime(14, 50, 10);
}
}
DigitalClock1 - Current time: 10:30:45
DigitalClock2 - Current time: 10:30:45
DigitalClock1 - Current time: 12:15:20
DigitalClock2 - Current time: 12:15:20
DigitalClock2 - Current time: 14:50:10
Observerパターンのメリットとデメリット
メリット
- 拡張性: 新しいObserverを簡単に追加できます
- 柔軟性: SubjectとObserverの間で疎結合を実現できます
- リアルタイム性: 状態変化が即座に反映されます
デメリット
- 通知のコスト: Observerが多いと,通知処理に時間がかかってしまいます
参考