JavaでProxyパターン

# はじめに
[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)を読んで、学んだ内容についてまとめます。

# Proxyパターン
## Proxyとは
日本語に訳すと「代理」を意味します。
**代理のクラスが本人のクラスに成り代わってある程度の処理を行い、本当に本人が必要になった段階で本人のクラスの生成を行うパターン**のことを**Proxyパターン**と言います。 
このパターンを適用することで、本人のクラスの生成を行う際に時間がかかる初期化処理に時間がかかり、ユーザーに不満を与えるという問題を解消することができます。

## 登場人物
Proxyパターン使用するのは以下のクラス図に登場するクラスです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/278934/45911fcb-5a67-d420-98af-c364d292266b.png)

### 抽象クラス/インタフェース
- **Subject**   
`Client`クラスからみて`Proxy`クラスと`RealSubject`クラスを同様に扱う(同一視する)ための抽象クラス/インタフェースです。  
実装はサブクラスの`Proxy`クラス、`RealSubject`クラスで行います。

### 実装クラス
- **Proxy**
`RealSubject`クラスの**代理役**となるクラスです。 
`Client`クラスからの要求に対してできるだけの処理を行い、本当に必要になった際に`RealSubject`クラスのインスタンスを生成します。 
また`Subject`クラス/インタフェースの実装を行い、フィールドとして本人役の`RealSubject`クラスを持ちます。

- **RealSubject**
**本人役**となるクラスです。 
`Proxy`クラスで要求を処理しきれなかった際に生成されます。また、`Proxy`クラスと同様に`Subject`クラス/インタフェースの実装を行います。

- **Client**
`Proxy`クラスを経由して`RealSubject`クラスを使用するクラスです。


## 具体例
具体例として、以下のクラスをもとに説明します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/278934/e8eb801e-fcfd-5d2a-f1bc-3493634e4921.png)

### インタフェース
- **Managementインタフェース**  

```java:Management.java
public interface Management {
	void setReviewProjectName(String projectName); 

	String getReviewProjectName();

	void printReviewResult();
}

```
`Management`インターフェースではprojectNameの設定・取得、またレビューを行った結果を表示するメソッドを定義しています。
実際の処理については後述の`Manager`クラス、`ManagerProxy`クラスで実装します。
特段難しい点はありません。

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

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

public class Manager implements Management {
	private String projectName;

	public Manager() {
		this.projectName = "無題の案件";
		heavyJob("Managementのインスタンスを生成し、Rv中");
	}

	public Manager(String projectName) {
		this.projectName = projectName;
		heavyJob("Managementのインスタンス(" + projectName + ")を生成し、Rv中");
	}

	@Override
	public void setReviewProjectName(String projectName) {
		this.projectName = projectName;
	}

	@Override
	public String getReviewProjectName() {
		return projectName;
	}

	private void heavyJob(String msg) { // 1.生成時に必要(という設定)な重い処理
		System.out.print(msg);
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
			System.out.print("・");
		}
		System.out.println("Rv完了。");
	}

	@Override
	public void printReviewResult() { // 2.乱数によってreviewの結果を振り分けて出力する処理
		System.out.println("----------Rv結果----------");
		Random rnd = new Random();
		int ran = rnd.nextInt(10);
		if (ran < 5) {
			System.out.println(projectName + "は却下されました。\n");
		} else if (ran > 5) {
			System.out.println(projectName + "は承認されました。\n");
		}
	}

}
```
`Manager`クラスは本人役のクラスです。代理役である`ManegerProxy`クラスには手に負えない処理を行います。
ここでは`printReviewResult`がその処理に当たります。
ポイントは以下の2点です。
  **1.**コンストラクタ内で生成時に必要(という設定)だが、時間の掛かる重い処理である`heavyJob`メソッドを呼んでいる。
  **2.**`printReviewResult`をオーバーライドし、reviewの結果を表示している。

補足の説明を行います。
`Manager`クラスの生成には初期設定として`heavyJob`メソッドを実行する必要があります。しかし、この処理は重い処理であり、時間もかかるため、代理人役である`ManegerProxy`クラスでは手に負えない処理(`printReviewResult`)が呼ばれるまでは`Manager`クラスを生成するのは得策ではありません。そのため後述の`ManegerProxy`クラスではプロジェクト名を取得・設定するだけの処理(`getReviewProjectName`,`setReviewProjectName`)は代理人役の自身が行い、`printReviewResult`が呼ばれた際に、本人役の`Maneger`クラスを生成・設定します。

- **ManagerProxyクラス**  

```java:ManagerProxy.java
public class ManagerProxy implements Management {
	private String projectName;
	private Manager real;

	public ManagerProxy() {
		this.projectName = "無題の案件";
	}

	public ManagerProxy(String projectName) {
		this.projectName = projectName;
	}

	@Override
	public synchronized void setReviewProjectName(String projectName) {
		// 1.本人役が生成・設定されていれば本人役にプロジェクト名を設定する。
		if (real != null) {
			real.setReviewProjectName(projectName);
		}
		// 1.本人役が生成・設定されていなければ代理人役にプロジェクト名を設定する。
		this.projectName = projectName;
	}

	@Override
	public String getReviewProjectName() {
		return projectName;
	}

	@Override
	public void printReviewResult() {
		realize();
		real.printReviewResult();
	}

	private synchronized void realize() {// 2.本人役が生成・設定されていない場合に本人役を生成・設定する
		if (real == null) {
			real = new Manager(projectName);
		}
	}
}
```
`ManagerProxy`クラスは`Manager`クラスの代理人役となるクラスです。
ポイントは以下の2点です。
**1.**`setReviewProjectName`をオーバーライドしている。
**2.**`printReviewResult`メソッド内で`realize`メソッドを呼んだ後に、本人役の`printReviewResult`メソッドを呼んでいる。

補足で説明を行います。
1.に関して`setReviewProjectName`が呼ばれた際に、本人役の`Manager`クラスが設定・生成されていない場合は、代理人役である`ManagerProxy`の`projectName`に値を設定します。この時、本人役の`Manager`クラスは生成されておらず、代理人役である`ManagerProxy`が代わりに処理を行っていると言えます。
2.に関して`printReviewResult`メソッドは代理人役の手には負えない処理であるため、本人役の`Manager`クラスが生成・設定されていない場合に、生成・設定を行っています。

### 実行クラス
- **Clientクラス**
  
```java:Client.java
public class Client {
	public static void main(String[] args) {
		Management m1 = new ManagerProxy("A案件");
		System.out.println("プロジェクトRvを開始します。");
		System.out.println("Rv中のプロジェクトは" + m1.getReviewProjectName() + "です。");
		m1.setReviewProjectName("B案件");
		System.out.println("Rv中のプロジェクトは" + m1.getReviewProjectName() + "です。");
		m1.printReviewResult();

		Management m2 = new ManagerProxy();
		System.out.println("プロジェクトRvを開始します。");
		System.out.println("Rv中のプロジェクトは" + m2.getReviewProjectName() + "です。");
		m2.setReviewProjectName("C案件");
		System.out.println("Rv中のプロジェクトは" + m2.getReviewProjectName() + "です。");
		m2.printReviewResult();
	}
}
```
`ManagerProxy`クラスのインスタンスを生成し名前の取得を設定を行っています。その後、`printReviewResult`メソッドを呼び出すことで本人役の`Manager`クラスのインスタンスを生成し、処理を行っています。

### 実行結果
`Client.java`を実行した結果は以下になります。
重い処理(`heavyJob`)が実行された後に、乱数によってRv結果がランダムに出力されていることが分かります。

```:実行結果
プロジェクトRvを開始します。
Rv中のプロジェクトはA案件です。
Rv中のプロジェクトはB案件です。
Managementのインスタンス(B案件)を生成し、Rv中・・・・・Rv完了。
----------Rv結果----------
B案件は却下されました。

プロジェクトRvを開始します。
Rv中のプロジェクトは無題の案件です。
Rv中のプロジェクトはC案件です。
Managementのインスタンス(C案件)を生成し、Rv中・・・・・Rv完了。
----------Rv結果----------
C案件は承認されました。
```
## メリット
proxyパターンのメリットは以下になります。
**1.**初期化に必要な時間を短縮し、ユーザーの利便性を高めることができる。
**2.**代理人役と本人役に切り分けることで部品化が図れる。

# まとめ
本当に必要になるまで本人ではなく代理人に処理を行わせるProxyパターンに関して学びました。
以下でサンプルコードをアップしていますのでよろしければ参考にどうぞ。

- [Proxyサンプルコード](https://github.com/masayuki777/study/tree/master/DesignPatterns/21_Proxy)

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

- [[随時更新]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)