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
模式可以说是很好地践行了里氏替换原则。