定义
- 组装具有复杂结构的实例。
Builder模式中的登场角色
- Builder(建造者)
- ConcreteBuilder(具体的建造者)
- Director(监工),只调用在Builder角色中被定义的方法,不依赖于ConcreteBuilder角色如何实现。
Builder模式的类图
Builder模式的时序图
拓展思路的要点
- 谁知道什么?
这里强调了“可替换性”,因为可以替换,组件才具有高价值。
Main类不知道Builder类,只知道调用了Director类的construct方法,就可以完成工作。
Director类知道Builder类,但不知道具体使用了哪一个ConcreteBuilder类,由Main来传递进来,但是哪一个子类进来都可以执行程序。
- 设计时能够决定的事情和不能决定的事情
有必要让设计出的类能够尽可能灵活地应对近期可能发生的变化。
- 代码的阅读方法和修改方法
编程时,有时从零开始,但更多时候都是从现有代码的基础上进行增加和修改。
彻底理解后再动手修改。
相关的设计模式
在Builder模式中,Director角色控制Builder角色方法调用的顺序(使用委托)。
在Template Method模式中,父类控制子类方法调用的顺序(使用继承)。
- Composite模式(第11章)
有些情况下Builder模式生成的实例构成了Composite模式。
- Abstract Factory模式(第8章)
Builder模式和Abstract Factory模式都用于生成复杂的实例。
- Facade模式(第15章)
在Builder模式中,Director角色通过组合Builder角色中的复杂方法向外部提供可以简单生成实例的接口(API)(相当于示例程序中的construct方法)。
Facade模式中的Facade角色则是通过组合内部模块向外部提供可以简单调用的接口(API)。
代码
- Builder(建造者)
package build;
public abstract class Builder {
private boolean initialized = false;
public void makeTitle(String title) {
if (!initialized) {
buildTitle(title);
}
initialized = true;
}
public void makeString(String str) {
if (initialized) {
buildString(str);
} else {
System.out.println("未初始化");
}
}
public void makeItems(String[] items) {
if (initialized) {
buildItems(items);
} else {
System.out.println("未初始化");
}
}
public void close() {
if (initialized) {
buildDone();
} else {
System.out.println("未初始化");
}
// TODO close以后不能再调用其他方法
}
public abstract void buildTitle(String title);
public abstract void buildString(String str);
public abstract void buildItems(String[] items);
public abstract void buildDone();
}
- ConcreteBuilder(具体的建造者)
package build;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class FrameBuilder extends Builder implements ActionListener {
private JFrame frame = new JFrame();
private Box box = new Box(BoxLayout.Y_AXIS);
@Override
public void buildTitle(String title) {
frame.setTitle(title);
}
@Override
public void buildString(String str) {
box.add(new JLabel(str));
}
@Override
public void buildItems(String[] items) {
Box innerbox = new Box(BoxLayout.Y_AXIS);
for (int i = 0; i < items.length; i++) {
JButton button = new JButton(items[i]);
button.addActionListener(this);
innerbox.add(button);
}
box.add(innerbox);
}
@Override
public void buildDone() {
frame.getContentPane().add(box);
frame.pack();
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.getActionCommand());
}
public JFrame getResult() {
return frame;
}
}
package org.suych.db.build;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class HTMLBuilder extends Builder {
private String filename;
private PrintWriter write;
@Override
public void buildTitle(String title) {
filename = title + ".html";
try {
write = new PrintWriter(new FileWriter(filename));
} catch (IOException e) {
e.printStackTrace();
}
write.println("<html><head><title>" + title + "</title></head></body>");
write.println("<h1>" + title + "</h1>");
}
@Override
public void buildString(String str) {
write.println("<p>" + str + "</p>");
}
@Override
public void buildItems(String[] items) {
write.println("<ul>");
for (int i = 0; i < items.length; i++) {
write.println("<li>" + items[i] + "</li>");
}
write.println("</ul>");
}
@Override
public void buildDone() {
write.println("</body></html>");
write.close();
}
public String getResult() {
return filename;
}
}
package build;
public class TextBuilder extends Builder {
private StringBuffer buffer = new StringBuffer();
@Override
public void buildTitle(String title) {
buffer.append("=======================\n");
buffer.append("【" + title + "】\n");
buffer.append("\n");
}
@Override
public void buildString(String str) {
buffer.append('*' + str + "\n");
buffer.append("\n");
}
@Override
public void buildItems(String[] items) {
for (int i = 0; i < items.length; i++) {
buffer.append(" ." + items[i] + "\n");
}
buffer.append("\n");
}
@Override
public void buildDone() {
buffer.append("=======================\n");
}
public String getResult() {
return buffer.toString();
}
}
- Director(监工)
package build;
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
};
public void construct() {
builder.makeTitle("Greeting");
builder.makeString("从早上至下午");
builder.makeItems(new String[] { "早上好。", "下午好" });
builder.makeString("晚上");
builder.makeItems(new String[] { "晚上好。", "晚安。", "再见。" });
builder.close();
}
}
- Main(调用者)
package build;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
TextBuilder textBuilder = new TextBuilder();
Director director = new Director(textBuilder);
director.construct();
String result = textBuilder.getResult();
System.out.println(result);
HTMLBuilder htmlBuilder = new HTMLBuilder();
Director director2 = new Director(htmlBuilder);
director2.construct();
String filename = htmlBuilder.getResult();
System.out.println(filename + "文件编写完成");
FrameBuilder frameBuilder = new FrameBuilder();
Director director4 = new Director(frameBuilder);
director4.construct();
JFrame frame = frameBuilder.getResult();
frame.setVisible(true);
}
}
注:博客中的图片来自网上。