在Java中使用外观模式
首先
我将总结我通过阅读《Java语言版设计模式入门》这本介绍GoF设计模式的扩充改进版书籍所学到的内容。
外观模式
Facade是什么意思?
日本語での訳: “正面” は建物の表面を意味します。
当プログラムには、複数のクラスが関わる場合、各クラスのメソッドを適切な順序で実行する必要があります。
例えば、処理 X を行いたい場合、A クラスの B メソッドを呼び出した後、C クラスの D メソッドと E メソッドを呼び出し、最後に F クラスの G メソッドを呼び出します。
上記の例では、クラスとメソッドの数はそれほど多くありませんでしたが、これらがより多くなると、処理の制御はより複雑になります。
このような場合に、処理を依頼する側が理解しやすくするために、”窓口” となるインタフェース(API)を提供することが、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类是Main类的接口类。只定义了一个静态的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;
}
}
这是其他两个类之一。
只定义了静态的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();
}
}
這是其他類別的第二個類別。
這裡定義了幾個方法,其中包括接收屬性並將其存儲在Map中的方法,以及用於將數據輸出到文本文件中的方法,但對於這個類別,沒有特別困難的地方。
其他
- テキストファイル
#name=age
Tanaka_Tarou=25
Hoge_Hogeko=10
Yamada_Hanako=30
Neko_Nekota=50
Foo_Footarou=80
这是一个用于加载MemberList类的文本文件。其中,名称作为键,年龄作为值对应。
执行类
- 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类中的静态方法makeMemberList即可执行所需的处理。同样,如果需要进行多次处理,只需再次调用该方法即可。
2. 如果未应用Facade模式,则必须直接复制ListFacade类中的内容才能执行所需的处理。另外,如果需要进行多次处理,则需要编写复杂的处理代码,次数与处理次数相匹配。
(※在这里,我们从Main类中使用其他类,但也可能通过分组包并指定protected,只能从窗口使用其他类。在这里,为了更容易理解使用Facade和不使用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. 可以防止意外的调用导致bug。
3. 通过限制接口调用来实现与外部的低耦合,提高可重用性。
总结
我学习了Facade模式,它提供了一个控制复杂处理的接口窗口。以下是我上传的样例代码,如果可以的话,请参考一下。
- Facadeサンプルコード
另外,关于其他设计模式,已经在以下进行了总结,请参考。
- [随時更新]Javaでデザインパターンまとめ
参考资料
- 増補改訂版 Java言語で学ぶデザインパターン入門