はじめに
GoFのデザインパターンを紹介している『増補改訂版 Java言語で学ぶデザインパターン入門』を読んで、学んだ内容についてまとめます。
Facadeパターン
Facadeとは
日本語に訳すと「(建物の)正面」を意味します。
複数のクラスが絡むプログラムを使用する場合には、それぞれのクラスのメソッドを適切な順番で実行する必要があります。
例えば「処理Xを行いたい場合にはAクラスのBメソッドを呼んだ後に、CクラスのDメソッドとEメソッドを呼び、最後にFクラスのGメソッドを呼ぶ」などです。
上記の例ではクラスもメソッドもそれほど多くありませんでしたが、これがもっと多くの数になればなるほど、処理を行う際のクラスとメソッドの制御は複雑になっていきます。
このような場合に、処理を依頼する側から見た際に「窓口」となるインタフェース(API)を用意してあげるようなパターンのことをFacadeパターンと言います。
このパターンを適用することで、処理を依頼する側は複雑な処理の制御を把握する必要がなくなります。
登場人物
Facadeパターン使用するのは以下のクラス図に登場するクラスです。
実装クラス
-
Facade
処理を行うためのクラスをまとめるクラスで、Clientに対しての窓口となります。
実装すべき内容に関しては特に縛りなどはなく、難しい点もありません。 -
その他クラス
処理を行うために呼び出されるクラスたちです。
実装すべき内容に関しては特に縛りなどはありません。
特段難しい点はありませんが、ポイントとしては使用されるクラスたちは窓口となるFacadeのことを意識しないことです。
その他のクラスたちからFacadeのことを呼び出すことはないためです。 -
Client
Facadeを呼び出すクラスです。
実装すべき内容に関しては特に縛りなどはなく、難しい点もありません。
具体例
実装クラス
- ListFacadeクラス
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クラス
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クラス
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に格納するメソッドや、テキストファイルに出力するためのメソッドなどいくつかメソッドが定義されていますが、こちらに関しても特段難しい点はありません。
その他
- テキストファイル
#name=age
Tanaka_Tarou=25
Hoge_Hogeko=10
Yamada_Hanako=30
Neko_Nekota=50
Foo_Footarou=80
MemberList
クラスの読み込みの対象となるテキストファイルです。
keyに名前、valueに年齢が当てはまります。
実行クラス
- Mainクラス
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
***************Header***************
年齢が25歳を超えるメンバーは以下です。
・名前:Yamada_Hanako 年齢:30
・名前:Foo_Footarou 年齢:80
・名前:Neko_Nekota 年齢:50
***************Footer***************
***************Header***************
年齢が25歳を超えるメンバーは以下です。
・名前:Yamada_Hanako 年齢:30
・名前:Foo_Footarou 年齢:80
・名前:Neko_Nekota 年齢:50
***************Footer***************
メリット
Facadeパターンのメリットは以下になります。
**1.**呼び出し側で詳細な制御を記述する必要がないので、コードをシンプルにすることができる。
**2.**意図しない呼び出しを行いバグになってしまうということを防ぐことができる。
**3.**インタフェースの呼び出し口を絞ることで、外部と疎結合にすることができ、再利用性が高まる。
まとめ
複雑な処理の制御を行う窓口を提供するFacadeパターンに関して学びました。
以下でサンプルコードをアップしていますのでよろしければ参考にどうぞ。
また、他のデザインパターンに関しては以下でまとめていますので、こちらも参考にどうぞ。