Builder 模式
在《圖解設計模式》這本書中,對Builder
模式的解釋是,用於組裝具有複雜結構的實例的設計模式。在這裏,我通過一個接地氣的小例子,來描述一下Builder
設計模式究竟做了什麼。
加入有一天,你突然想蓋一棟別墅,首先你需要找一個會蓋房子的建築師,告訴他“給我蓋一個別墅”,讓他幫你蓋這棟房子。
建築師並不會直接的蓋房子,他只會指揮施工隊蓋房子。它會指揮調度施工隊,先打好地基,然後搭好框架,然後一層一層蓋房子,最後封頂,房子蓋完了。
那麼,建築師需要知道施工隊是具體怎麼打地基的麼?是用左手挖地基還是右手挖地基,是用空心磚蓋房子還是用實心磚蓋房子呢?這些建築師都不需要知道,就像你不需要知道建築師是怎麼指揮施工隊蓋房子的呢。
這樣的好處是什麼呢?因爲建築師僅僅需要知道怎麼調度施工隊,而不需要聚焦於具體怎麼施工,所以有一天當另外一個施工隊來替換當前的施工隊的時候,他依然可以很好的指揮施工隊。
同時,如果有一天這個姓王的建築師不在了,你也僅僅只需要找一個姓李的建築師,同時也原封不動的告訴他“給我蓋一個別墅”,你依然可以蓋一棟別墅。
我們來看一段代碼:
與蓋房子不同的是,我們現在想要的是編寫一個文件。首先有一個Builder
類,他知道編寫文檔的各個流程。
public abstract class Builder {
public abstract void makeTitle(String title);
public abstract void makeString(String str);
public abstract void makeItems(String[] items);
public abstract void close();
}
其次有一個Director
類,他講指揮一個類來編寫文件:
public class Director {
private Builder builder;
public Director(Builder builder) { // 因爲接收的參數是Builder類的子類
this.builder = builder; // 所以可以將其保存在builder字段中
}
public void construct() { // 編寫文檔
builder.makeTitle("Greeting"); // 標題
builder.makeString("從早上至下午"); // 字符串
builder.makeItems(new String[]{ // 條目
"早上好。",
"下午好。",
});
builder.makeString("晚上"); // 其他字符串
builder.makeItems(new String[]{ // 其他條目
"晚上好。",
"晚安。",
"再見。",
});
builder.close(); // 完成文檔
}
}
Directo
直接調用的抽象的Builder
類,而不是具體的某個Builder
。就像建築師在蓋房子的時候,不會直接點名道姓的找XX施工隊
一樣,他只需要知道自己要找一個叫施工隊的團體。
最後,一個具體的Builder
類,也就是你在蓋房子過程中爲你的建築師找到的具體的那個施工隊
import java.io.*;
public class HTMLBuilder extends Builder {
private String filename; // 文件名
private PrintWriter writer; // 用於編寫文件的PrintWriter
@Override
public void makeTitle(String title) { // HTML文件的標題
filename = title + ".html"; // 將標題作爲文件名
try {
writer = new PrintWriter(new FileWriter(filename)); // 生成 PrintWriter
} catch (IOException e) {
e.printStackTrace();
}
writer.println("<html><head><title>" + title + "</title></head><body>"); // 輸出標題
writer.println("<h1>" + title + "</h1>");
}
@Override
public void makeString(String str) { // HTML文件中的字符串
writer.println("<p>" + str + "</p>"); // 用<p>標籤輸出
}
@Override
public void makeItems(String[] items) { // HTML文件中的條目
writer.println("<ul>"); // 用<ul>和<li>輸出
for (int i = 0; i < items.length; i++) {
writer.println("<li>" + items[i] + "</li>");
}
writer.println("</ul>");
}
@Override
public void close() { // 完成文檔
writer.println("</body></html>"); // 關閉標籤
writer.close(); // 關閉文件
}
public String getResult() { // 編寫完成的文檔
return filename; // 返回文件名
}
}
Main.java
public class Main {
public static void main(String[] args) {
HTMLBuilder htmlbuilder = new HTMLBuilder();
Director director = new Director(htmlbuilder);
director.construct();
String filename = htmlbuilder.getResult();
System.out.println(filename + "文件編寫完成。");
}
}
輸出如下:
Greeting.html文件編寫完成
Greeting.html
<html><head><title>Greeting</title></head><body> <h1>Greeting</h1> <p>從早上至下午</p> <ul> <li>早上好。</li> <li>下午好。</li> </ul> <p>晚上</p> <ul> <li>晚上好。</li> <li>晚安。</li> <li>再見。</li> </ul> </body></html>
Builder模式
具體的UML類圖:
時序圖:
Builder
模式可以說是很好地踐行了里氏替換原則。