Javaにおけるオブザーバーパターン:Javaアプリケーションでのリアクティブインターフェースの習得
別名
- 依存関係
オブザーバーデザインパターンの意図
Javaにおけるオブザーバーパターンは、オブジェクト間の一対多の関係を定義し、1つのオブジェクトが状態を更新すると、すべての依存オブザーバーに自動的に通知され更新されることを保証し、システムの応答性とモジュール性を向上させます。
現実世界の例を用いたオブザーバーパターンの詳細な説明
現実世界の例
現実世界の例として、ニュース機関(サブジェクト)がニュース記事を公開し、複数のニュース配信機関(オブザーバー)が更新を受け取るために購読するニュース機関システムを考えてみましょう。ニュース機関が新しい記事を公開するたびに、購読しているすべてのニュース配信機関に自動的に通知します。これらの配信機関は、最新のニュースでプラットフォーム(ウェブサイト、テレビ放送、新聞など)を更新できます。これにより、ニュース機関は各配信機関の更新プロセスの詳細を知る必要なく、すべての購読者が最新の情報を入手できます。これは、ニュース機関を購読者から切り離し、更新の処理方法において柔軟性とモジュール性を促進します。
平易な言葉で
Javaアプリケーションでの状態の変化を積極的に監視し、対応するためにオブザーバーインターフェースを実装し、イベント駆動型プログラミングの効率を向上させます。
Wikipediaの説明
オブザーバーパターンは、サブジェクトと呼ばれるオブジェクトが、オブザーバーと呼ばれる依存オブジェクトのリストを保持し、通常はメソッドの1つを呼び出すことによって、状態の変更を自動的に通知するソフトウェアデザインパターンです。
Javaでのオブザーバーパターンのプログラム例
遠い昔の地には、ホビットとオークの種族が住んでいます。彼らはどちらも主に屋外にいるため、天気の変化を注意深く追っています。彼らは常に天気を観察していると言えるでしょう。
まず、WeatherObserver
インターフェースと、私たちの種族であるOrcs
とHobbits
を紹介しましょう。
public interface WeatherObserver {
void update(WeatherType currentWeather);
}
@Slf4j
public class Orcs implements WeatherObserver {
@Override
public void update(WeatherType currentWeather) {
LOGGER.info("The orcs are facing " + currentWeather.getDescription() + " weather now");
}
}
@Slf4j
public class Hobbits implements WeatherObserver {
@Override
public void update(WeatherType currentWeather) {
switch (currentWeather) {
LOGGER.info("The hobbits are facing " + currentWeather.getDescription() + " weather now");
}
}
}
次に、常に変化しているWeather
を示します。
@Slf4j
public class Weather {
private WeatherType currentWeather;
private final List<WeatherObserver> observers;
public Weather() {
observers = new ArrayList<>();
currentWeather = WeatherType.SUNNY;
}
public void addObserver(WeatherObserver obs) {
observers.add(obs);
}
public void removeObserver(WeatherObserver obs) {
observers.remove(obs);
}
/**
* Makes time pass for weather.
*/
public void timePasses() {
var enumValues = WeatherType.values();
currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length];
LOGGER.info("The weather changed to {}.", currentWeather);
notifyObservers();
}
private void notifyObservers() {
for (var obs : observers) {
obs.update(currentWeather);
}
}
}
これが、実行中の完全な例です。
public static void main(String[] args) {
var weather = new Weather();
weather.addObserver(new Orcs());
weather.addObserver(new Hobbits());
weather.timePasses();
weather.timePasses();
weather.timePasses();
weather.timePasses();
// Generic observer inspired by Java Generics and Collections by Naftalin & Wadler
LOGGER.info("--Running generic version--");
var genericWeather = new GenWeather();
genericWeather.addObserver(new GenOrcs());
genericWeather.addObserver(new GenHobbits());
genericWeather.timePasses();
genericWeather.timePasses();
genericWeather.timePasses();
genericWeather.timePasses();
}
プログラムの出力
21:28:08.310 [main] INFO com.iluwatar.observer.Weather -- The weather changed to rainy.
21:28:08.312 [main] INFO com.iluwatar.observer.Orcs -- The orcs are facing Rainy weather now
21:28:08.312 [main] INFO com.iluwatar.observer.Hobbits -- The hobbits are facing Rainy weather now
21:28:08.312 [main] INFO com.iluwatar.observer.Weather -- The weather changed to windy.
21:28:08.312 [main] INFO com.iluwatar.observer.Orcs -- The orcs are facing Windy weather now
21:28:08.312 [main] INFO com.iluwatar.observer.Hobbits -- The hobbits are facing Windy weather now
21:28:08.312 [main] INFO com.iluwatar.observer.Weather -- The weather changed to cold.
21:28:08.312 [main] INFO com.iluwatar.observer.Orcs -- The orcs are facing Cold weather now
21:28:08.312 [main] INFO com.iluwatar.observer.Hobbits -- The hobbits are facing Cold weather now
21:28:08.312 [main] INFO com.iluwatar.observer.Weather -- The weather changed to sunny.
21:28:08.312 [main] INFO com.iluwatar.observer.Orcs -- The orcs are facing Sunny weather now
21:28:08.312 [main] INFO com.iluwatar.observer.Hobbits -- The hobbits are facing Sunny weather now
21:28:08.312 [main] INFO com.iluwatar.observer.App -- --Running generic version--
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenWeather -- The weather changed to rainy.
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenOrcs -- The orcs are facing Rainy weather now
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenHobbits -- The hobbits are facing Rainy weather now
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenWeather -- The weather changed to windy.
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenOrcs -- The orcs are facing Windy weather now
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenHobbits -- The hobbits are facing Windy weather now
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenWeather -- The weather changed to cold.
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenOrcs -- The orcs are facing Cold weather now
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenHobbits -- The hobbits are facing Cold weather now
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenWeather -- The weather changed to sunny.
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenOrcs -- The orcs are facing Sunny weather now
21:28:08.313 [main] INFO com.iluwatar.observer.generic.GenHobbits -- The hobbits are facing Sunny weather now
Javaでオブザーバーパターンを使用するタイミング
次のいずれかの状況でオブザーバーパターンを使用します
- 抽象化に2つの側面があり、一方が他方に依存している場合。これらの側面を別々のオブジェクトにカプセル化すると、独立して変化させて再利用できます。
- 1つのオブジェクトへの変更が他のオブジェクトの変更を必要とする場合、および変更する必要があるオブジェクトの数が不明な場合。
- オブジェクトが、それらのオブジェクトが誰であるかについての仮定をすることなく、他のオブジェクトに通知できる必要がある場合。言い換えれば、これらのオブジェクトが密接に結合していることを望まない場合。
Javaでのオブザーバーパターンの現実世界の応用
- java.util.Observer
- java.util.EventListener
- javax.servlet.http.HttpSessionBindingListener
- RxJava
- モデルビューコントローラー(MVC)フレームワーク。
- イベント処理システム。
オブザーバーパターンの利点とトレードオフ
利点
- このJavaデザインパターンは疎結合を促進し、サブジェクトとそのオブザーバーが緊密な依存関係なしに対話できるようにし、メンテナンスとスケーラビリティを容易にします。
- オブザーバーの動的なサブスクリプションとサブスクリプション解除を許可します。
トレードオフ
- オブザーバーが適切に登録解除されない場合、メモリリークにつながる可能性があります。
- 通知の順序は指定されていないため、予期しない動作が発生する可能性があります。
- 多数のオブザーバーを使用すると、パフォーマンス上の問題が発生する可能性があります。
関連するJavaデザインパターン
- メディエーター: オブジェクトのセットがどのように相互作用するかをカプセル化し、オブジェクト間の直接的な依存関係を減らすために使用できます。
- シングルトン: サブジェクトの単一のインスタンスを確保するために、オブザーバーパターンでよく使用されます。