JavaでFacadeパターン

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

# Facadeパターン
## Facadeとは
日本語に訳すと「(建物の)正面」を意味します。
複数のクラスが絡むプログラムを使用する場合には、それぞれのクラスのメソッドを適切な順番で実行する必要があります。
例えば「処理Xを行いたい場合にはAクラスのBメソッドを呼んだ後に、CクラスのDメソッドとEメソッドを呼び、最後にFクラスのGメソッドを呼ぶ」などです。
上記の例ではクラスもメソッドもそれほど多くありませんでしたが、これがもっと多くの数になればなるほど、**処理を行う際のクラスとメソッドの制御は複雑になっていきます。**
このような場合に、**処理を依頼する側から見た際に「窓口」となるインタフェース(API)を用意してあげるようなパターン**のことを**Facadeパターン**と言います。
このパターンを適用することで、処理を依頼する側は複雑な処理の制御を把握する必要がなくなります。

## 登場人物
Facadeパターン使用するのは以下のクラス図に登場するクラスです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/278934/99e0c1eb-8db8-f67d-cf1a-45f5b612f88f.png)

### 実装クラス
- **Facade**
処理を行うためのクラスをまとめるクラスで、**Clientに対しての窓口**となります。
実装すべき内容に関しては特に縛りなどはなく、難しい点もありません。

- **その他クラス**
処理を行うために呼び出されるクラスたちです。
実装すべき内容に関しては特に縛りなどはありません。
特段難しい点はありませんが、ポイントとしては使用されるクラスたちは窓口となるFacadeのことを意識しないことです。
その他のクラスたちからFacadeのことを呼び出すことはないためです。

- **Client**
Facadeを呼び出すクラスです。
実装すべき内容に関しては特に縛りなどはなく、難しい点もありません。

## 具体例
具体例として、以下のクラスをもとに説明します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/278934/25409881-e97e-795e-97f3-810751570b9a.png)


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

```java:ListFacade.java
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;

public class ListFacade {
	private ListFacade() { // newでインスタンス生成させないためにprivate宣言
	}

	public static void makeMemberList(String inputFileName, String outputFileName) {
		try {
			Properties memberProperties = MemberList.getProperties(inputFileName);
			outputFileName = "C:\\work\\Facade\\src\\" + outputFileName + ".txt";
			TxtWriter writer = new TxtWriter(new FileWriter(outputFileName));
			Map<String, String> propertiesMap = writer.toMap(memberProperties);
			writer.writeHeader();
			writer.writeContents(propertiesMap);
			writer.writeFooter();
			writer.close();
			System.out.println(String.format("ファイルを出力しました。ファイル名:%s", outputFileName));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
```
`ListFacade`クラスはClientである`Main`クラスの窓口となるクラスです。
statisな`makeMemberList`メソッドのみが定義されており、`MemberList`クラスからプロパティを受け取り、そのプロパティをもとにMapを受け取り、ファイルに出力していることが分かります。
また処理が成功した場合にはファイルを出力した旨のメッセージをコンソールに出力しています。
ポイントとしては、**これらの一連の処理の流れを`Main`クラスではなく、窓口である`ListFacade`クラスに記述してあるという点のみ**です。
パターンの学習として、他に関しては特段難しい点はありません。

- **MemberListクラス**  

```java:MemberList.java
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class MemberList {
	private MemberList() { // newでインスタンス生成させないためにprivate宣言
	}

	public static Properties getProperties(String fileName) { // ファイル名からPropertiesを得る
		fileName = "C:\\work\\Facade\\src\\" + fileName + ".txt";
		Properties properties = new Properties();
		try {
			properties.load(new FileInputStream(fileName));
		} catch (IOException e) {
			System.out.println(String.format("ファイルの読み込みに失敗しました。ファイル名:%s", fileName));
		}
		return properties;
	}
}
```
2つあるその他のクラスの1つ目です。
staticな`getProperties`メソッドのみが定義されており、ファイル名を受け取ることでプロパティファイルを返しています。
特段難しい点はありません。

- **TxtWriterクラス**  

```java:TxtWriter.java
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

public class TxtWriter {
	private Writer writer;

	public TxtWriter(Writer writer) { // コンストラクタ
		this.writer = writer;
	}

	// プロパティを受け取り値(キー)が25を超えるものをMapに格納
	public Map<String, String> toMap(Properties properties) {
		Map<String, String> propertiesMap = new HashMap<>();
		for (Entry<Object, Object> e : properties.entrySet()) {
			if (Integer.parseInt((String) e.getValue()) > 25) {
				propertiesMap.put(e.getKey().toString(), e.getValue().toString());
			}
		}
		return propertiesMap;
	}

	// ヘッダーを出力
	public void writeHeader() throws IOException {
		writer.write("***************Header***************");
		writer.write(System.lineSeparator());
	}

	// 内容を出力
	public void writeContents(Map<String, String> propertiesMap) throws IOException {
		writer.write("年齢が25歳を超えるメンバーは以下です。");
		writer.write(System.lineSeparator());
		for (Entry<String, String> e : propertiesMap.entrySet()) {
			writer.write("　・名前：" + e.getKey() + "　年齢：" + e.getValue());
			writer.write(System.lineSeparator());
		}
	}

	// フッターを出力
	public void writeFooter() throws IOException {
		writer.write("***************Footer***************");
	}

	// 閉じる
	public void close() throws IOException {
		writer.close();
	}

}
```
その他のクラスの2つ目です。
プロパティを受け取ってMapに格納するメソッドや、テキストファイルに出力するためのメソッドなどいくつかメソッドが定義されていますが、こちらに関しても特段難しい点はありません。

### その他
- **テキストファイル**

```properties:memberList.txt
#name=age
Tanaka_Tarou=25
Hoge_Hogeko=10
Yamada_Hanako=30
Neko_Nekota=50
Foo_Footarou=80
```
`MemberList`クラスの読み込みの対象となるテキストファイルです。
keyに名前、valueに年齢が当てはまります。

### 実行クラス
- **Mainクラス**
  
```java:Main.java
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;

public class Main {
	public static void main(String[] args) {

		// 1.Facadeパターンを適用した場合
		ListFacade.makeMemberList("memberList", "outputFile1");

		// 2.Facadeパターンを未適用の場合
		try {
			String inputFileName = "memberList";
			String outputFileName = "outputFile2";

			Properties memberProperties = MemberList.getProperties(inputFileName);
			outputFileName = "C:\\work\\Facade\\src\\" + outputFileName + ".txt";
			TxtWriter writer = new TxtWriter(new FileWriter(outputFileName));
			Map<String, String> propertiesMap = writer.toMap(memberProperties);
			writer.writeHeader();
			writer.writeContents(propertiesMap);
			writer.writeFooter();
			writer.close();
			System.out.println(String.format("ファイルを出力しました。ファイル名:%s", outputFileName));
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}
```
**1.Facadeパターンを適用した場合**だと、窓口である`ListFacade`クラスに対してstaticな`makeMemberList`メソッドを呼び出してあげるだけで、目的の処理を実行することができます。
また処理を複数回行いたい場合にも同様にメソッドを呼び出してあげるだけで済みます。、
**2.Facadeパターンを未適用の場合**の場合には、`ListFacade`クラスに記述してある内容をそのまま記述しないと、目的の処理を実行することはできません。
また、処理を複数回行いたい場合には、処理を行いたい数だけ、複雑な処理を記述する必要があります。

(**※今回はMainクラスからその他のクラスを利用していますが、パッケージを分けてprotectedを指定することで窓口からしかその他のクラスを利用できなくする**ということもあり得ます。
ここではFacadeを利用した場合と利用しない場合の違いが分かりやすいように、Clientからも呼び出せるようにしています。)

### 実行結果
`Main.java`を実行した結果は以下になります。
パターンを適用した場合と未適用の場合で、処理の結果には違いがないことが確認できます。

```:実行結果(コンソール)
ファイルを出力しました。ファイル名:C:\work\Facade\src\outputFile1.txt
ファイルを出力しました。ファイル名:C:\work\Facade\src\outputFile2.txt
```

```:実行結果(outputFile1.txt)
***************Header***************
年齢が25歳を超えるメンバーは以下です。
　・名前：Yamada_Hanako　年齢：30
　・名前：Foo_Footarou　年齢：80
　・名前：Neko_Nekota　年齢：50
***************Footer***************
```

```:実行結果(outputFile2.txt)
***************Header***************
年齢が25歳を超えるメンバーは以下です。
　・名前：Yamada_Hanako　年齢：30
　・名前：Foo_Footarou　年齢：80
　・名前：Neko_Nekota　年齢：50
***************Footer***************
```

## メリット
Facadeパターンのメリットは以下になります。
　**1.**呼び出し側で詳細な制御を記述する必要がないので、コードをシンプルにすることができる。
　**2.**意図しない呼び出しを行いバグになってしまうということを防ぐことができる。
　**3.**インタフェースの呼び出し口を絞ることで、外部と疎結合にすることができ、再利用性が高まる。

# まとめ
複雑な処理の制御を行う窓口を提供するFacadeパターンに関して学びました。
以下でサンプルコードをアップしていますのでよろしければ参考にどうぞ。

- [Facadeサンプルコード](https://github.com/masayuki777/study/tree/master/DesignPatterns/15_Facade)

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

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