JavaでObserverパターン

# はじめに
[GoF](https://ja.wikipedia.org/wiki/ギャング・オブ・フォー_(情報工学))のデザインパターンを紹介している[『増補改訂版 Java言語で学ぶデザインパターン入門』](https://www.amazon.co.jp/増補改訂版Java言語で学ぶデザインパターン入門-結城-浩/dp/4797327030/ref=pd_lpo_sbs_14_t_0?_encoding=UTF8&psc=1&refRID=2ZE4GPYNN55JGDR5QMHP)を読んで、学んだ内容についてまとめます。

# Observerパターン
## Observerとは
「観察(observe)する人」のことで「観察者」を意味します。
**観察する対象の状態が変化する際に観察者に対して通知を行うパターン**のことを**Observerパターン**と言います。
詳しくは後述しますが、観察する対象が観察者に対して通知を行う必要があり、観察者は受動的な立場であることから、**Publish-Subscribeパターン**と呼ばれることもあるようです。

## 登場人物
Observerパターン使用するのは以下のクラス図に登場するクラスです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278934/db37f1b6-6f3d-3c12-0dd0-cd70dd19cdbd.png)


### 抽象クラス
- **Subject**
観察される側となるクラスです。
自身を観察する`observers`(複数のobserver)とそれらを登録・削除・通知するメソッドや自身の状態を返すメソッドを持ちます。
先ほど述べたように、**観察される側であるはずsubjectが観察する側のobserverを保持している点**が注意すべき点です。

- **Observer**
`subject`を観察する側のクラスです。
観察と言ってもsubjectから通知されて初めて観察対象の状態が変化したことを知ります。

### 実装クラス
- **ConcreteSubject**
具体的な観察される側となるクラスです。
親クラスで宣言されている、自身の状態を返す`getSubjectStatus`メソッドをオーバーライドします。

- **ConcreteObserveer**
具体的な観察する側となるクラスです。
親クラスで宣言されている、観察対象の状態が変化したことを通知されるための`update`メソッドをオーバーライドします。

## 具体例
具体例として、以下のクラスをもとに説明します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278934/ead85cd6-b176-f142-e755-b393fa6a6da8.png)

### 抽象クラス
- **NumGeneratorクラス**  

```java:NumGenerator.java
import java.util.ArrayList;
import java.util.List;

public abstract class NumGenerator {

	// ObserverのListを保持
	private List<Observer> observers = new ArrayList<>();

	// Observerを追加
	public void addObserver(Observer observer) {
		observers.add(observer);
	}

	// Observerを削除
	public void deleteObserver(Observer observer) {
		observers.remove(observer);
	}

	// Observerへ通知
	public void notifyObservers() {
		for (Observer observer : observers) {
			observer.update(this);
		}
	}

	// 数を取得する
	public abstract int getNumber();

	// 数を生成する(状態の更新)
	public abstract void execute();
}
```
観察される対象の`Subject`役となるクラスです。
観察者は複数存在することもあり得るため`Observer`をListで持っています。
また、観察者を登録・削除するメソッドと、登録者に通知を行うためのメソッドも持っています。
`notifyObservers`メソッドでは具体的な観察者たちに対して`update`メソッドを呼び出しています。

- **Observerクラス**  

```java:Observer.java
public interface Observer {
	public abstract void update(NumGenerator generator);
}
```
`NumGenerator`を観察する役となるクラスです。
通知を受け取るための`update`メソッドを宣言してますが、実装は継承するクラスに任せます。

### 実装クラス
- **RandomNumGeneratorクラス**  

```java:RandomNumGenerator.java
import java.util.Random;

public class RandomNumGenerator extends NumGenerator {
	// 乱数生成機
	private Random random = new Random();
	// 現在の数
	private int number;

	// 数を取得する
	@Override
	public int getNumber() {
		return number;
	}

	// 数を生成する(状態の更新)
	@Override
	public void execute() {
		for (int i = 0; i < 10; i++) {
			// 0～20の範囲で乱数を生成して現在の数の更新
			number = random.nextInt(21);
			System.out.println("ループカウント:" + (i + 1));
			// 数が生成されたことをobserverへ通知
			notifyObservers();
		}
	}
}
```
`NumGenerator`を継承するクラスで具体的な観察対象役となるクラスです。
乱数と現在の数を表すフィールドを持ち、数を取得するメソッドと数を生成するためのメソッドを持ちます。
数を生成するメソッドでは10回ループを回しており、各ループの中では0～20の範囲内で乱数を取得し、その乱数を現在の数値に設定することで状態の更新を行っています。
また、状態が更新されたことを`notifyObservers`メソッドで各observerに通知します。

- **DigitObserverクラス**  

```java:DigitObserver.java
public class DigitObserver implements Observer {
	@Override
	public void update(NumGenerator generator) {
		// 観察者が持つ乱数を取得して表示
		System.out.println("DigitObserver:" + generator.getNumber());
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
		}
	}
}
```
`Observer`を実装する具体的な観察者の役となるクラスで`update`メソッドをオーバーライドしています。
このメソッドは観察対象が更新を通知してきた際に具体的な観察者が行う処理を記述します。
ここでは観察対象が持つ乱数(更新された現在の数)を表示します。

- **StringObserverクラス**  

```java:StringObserver.java
import org.apache.commons.lang3.RandomStringUtils;

public class StringObserver implements Observer {
	@Override
	public void update(NumGenerator generator) {
		System.out.print("StringObserver:");
		// 観察者が持つ乱数を引数として、ランダムなアルファベット文字列を取得して表示
		System.out.println(RandomStringUtils.randomAlphabetic(generator.getNumber()));
		System.out.println("----------------------------------------");
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
		}
	}
}
```
DigitObserverと同様に具体的な観察者役となるクラスです。
ここでは観察対象が持つ乱数(更新された現在の数)を引数として、ランダムなアルファベットからなる文字列を表示しています。

### 実行クラス
- **Mainクラス**  

```java:Main.java
public class Main {
	public static void main(String[] args) {
		// 具体的な観察対象の生成
		NumGenerator generator = new RandomNumGenerator();
		// 具体的な観察対象に具体的な観察者を登録
		generator.addObserver(new DigitObserver());
		generator.addObserver(new StringObserver());
		// 数の生成(状態の更新)
		generator.execute();
	}
}
```
具体的な観察対象の生成と、具体的な観察者を観察対象に登録する処理を行った後、数の生成を行うことで状態を更新しています。

### 実行結果
`Main`クラスを実行した結果は以下となります。
乱数と乱数の桁数分のランダムな文字列を表示を10回行っていることが分かります。
※乱数を利用しているため表示結果は異なります。

```:実行結果
ループカウント:1
DigitObserver:14
StringObserver:VNXxKnJCmkbOSG
----------------------------------------
ループカウント:2
DigitObserver:15
StringObserver:FUBpVQotKbSwmMX
----------------------------------------
ループカウント:3
DigitObserver:6
StringObserver:onRlXn
----------------------------------------
ループカウント:4
DigitObserver:18
StringObserver:AehtMZiGkzYgapTgok
----------------------------------------
ループカウント:5
DigitObserver:8
StringObserver:XuRUWXnb
----------------------------------------
ループカウント:6
DigitObserver:11
StringObserver:JHYAeuMfMDO
----------------------------------------
ループカウント:7
DigitObserver:12
StringObserver:sopRShHkheIO
----------------------------------------
ループカウント:8
DigitObserver:11
StringObserver:BLATKGBDccR
----------------------------------------
ループカウント:9
DigitObserver:18
StringObserver:kmSHMbZZftRyGkpaqa
----------------------------------------
ループカウント:10
DigitObserver:15
StringObserver:muYkfeGLfwYqykD
----------------------------------------
```

## メリット
Observerパターンを利用することで、状態を保持するクラスと状態の変化を通知してもらうクラスを分けることができ、クラスの再利用性(部品化)が高まります。

## 使いどころ
Observerパターンの使いどころとして、MVCモデルの実装におけるモデルとビューの連携があげられます。
モデルは内部的なデータであり、コントローラーがモデル(観察対象)の状態の変化を検出し、ビュー(観察者)に通知することで連携を行います。

# まとめ
オブジェクトの状態の変化を通知するObserverパターンに関して学びました。
以下でサンプルコードをアップしていますのでよろしければ参考にどうぞ。

- [Observerサンプルコード](https://github.com/masayuki777/study/tree/master/DesignPatterns/17_Observer)

また、他のデザインパターンに関しては以下でまとめていますので、こちらも参考にどうぞ。

- [[随時更新]Javaでデザインパターンまとめ](https://qiita.com/mk777/items/4e13505b654fc21c76a2)

# 参考文献
- [増補改訂版 Java言語で学ぶデザインパターン入門](https://www.amazon.co.jp/増補改訂版Java言語で学ぶデザインパターン入門-結城-浩/dp/4797327030/ref=pd_lpo_sbs_14_t_0?_encoding=UTF8&psc=1&refRID=2ZE4GPYNN55JGDR5QMHP)