導入
気温が変わったら、スマホアプリにもテレビの天気表示にも、同時に反映してほしい——「1つの変化を、複数の相手に知らせたい」場面はよくあります。Observer(オブザーバー) は、変化を検知する側(Subject)が、登録された観察者(Observer)全員に自動で通知する パターンです。
説明
WeatherStation(観測所)は、自分に登録された Observer の一覧を持ち、気温が変わるたびに全員へ通知します。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
WeatherStation station = new WeatherStation();
station.addObserver(new PhoneDisplay());
station.addObserver(new TvDisplay());
station.setTemperature(28);
}
}
interface Observer {
void update(int temperature);
}
class PhoneDisplay implements Observer {
public void update(int temperature) {
System.out.println("[携帯] 気温: " + temperature + "度");
}
}
class TvDisplay implements Observer {
public void update(int temperature) {
System.out.println("[TV] 気温: " + temperature + "度");
}
}
class WeatherStation {
private List<Observer> observers = new ArrayList<>();
private int temperature;
void addObserver(Observer observer) {
observers.add(observer);
}
void setTemperature(int temperature) {
this.temperature = temperature;
notifyObservers();
}
private void notifyObservers() {
for (Observer o : observers) {
o.update(temperature);
}
}
}
classDiagram
Observer <|.. PhoneDisplay
Observer <|.. TvDisplay
WeatherStation --> "many" Observer : notifies
class Observer {
<<interface>>
+update(temperature)
}
WeatherStationはList<Observer> observersに登録された相手を覚えています。具体的にPhoneDisplayやTvDisplayが何個あるか、何であるかは知りません。「Observerを実装した何か」としか扱いません。station.addObserver(...)… 通知してほしい相手を、実行時に自由に追加できます。表示方法を増やしたいときはObserverを実装した新しいクラスを1つ足して登録するだけです。setTemperature(28)が呼ばれると、内部のnotifyObservers()が登録済み全員のupdate(temperature)を順番に呼び出します。- Singletonが「1つに制限する」パターンだったのに対し、Observerは「1つの変化を複数に伝播させる」パターンです。
やってみよう
下のエディタを実行し、気温28度が携帯・TVの両方に通知されることを確認しましょう。station.addObserver(...) の呼び出し順を変えると、通知される順番がどう変わるか試してみてください。
演習
WatchDisplay という Observer を追加してください([ウォッチ] 気温: X度 と表示)。main で station に PhoneDisplay・TvDisplay・WatchDisplay の3つを登録し、station.setTemperature(30) を呼んでください。
ヒント1を見る
class WatchDisplay implements Observer { public void update(int temperature) { System.out.println("[ウォッチ] 気温: " + temperature + "度"); } }。
ヒント2を見る
station.addObserver(new WatchDisplay()); を、他の2つと同じように setTemperature を呼ぶ前に追加します。