设计者模式之建造者模式
前言
在城市之中林立着各种各样的高楼大厦,这些高楼大厦都是具有建筑建构的大型建筑。而想要建造一栋这样的高楼大厦,需要打地基、搭框架,然后自下而上、循序渐进的一层一层盖起来。通常,这样具有这样复杂结构的物体很难一气呵成的建造出来,需要先建造各式各样的小零件,最终通过将这些小零件组装起来形成最终的复杂结构—产品。
定义
建造者模式:将一个复杂对象的构建与它的表示进行分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。一句话就是,将产品的构建过程产品本身相分离。
UML类图
- 在建造者模式中,一共有三类角色,分别为
建造者(Builder)
、具体建造者(ConcreteBuilder)
以及指挥者(Director)
,其作用分别是:- 建造者:其通常是一个抽象类或接口,定义了生成复杂实例的方法,具体实现交由子类来实现。如通过Builder类的makeTitle、makeString等方法组合起来可以生成
文档
这个复杂对象。 - 具体建造者:该类负责实现建造者类中定义的方法,如TextBuilder类要生成一个纯文本形式的文档,HTMLBuilder要生成一个html格式的文档。此外该角色中还定义了获取最终实例的方法–getResult()
- 指挥者:该角色使用Builder角色来生成实例,他只调用Builder类中定义的方法,而不关心ConcreteBuilder类中的方法,这也就保证了在建造复杂实例这块的程序具有”可替换性“。
- 建造者:其通常是一个抽象类或接口,定义了生成复杂实例的方法,具体实现交由子类来实现。如通过Builder类的makeTitle、makeString等方法组合起来可以生成
示例程序
Builder类
Builder类是一个声明了编写文档方法的抽象类。其中,makeTitle、makeString和makeItems分别是编写标题、字符串和条目的方法,而close是完成编写文档的方法。具体程序如下:
public abstract class Builder {
/**
* 编写字符串内容
* @param str
*/
public abstract void makeString(String str);
/**
* 编写标题
* @param title
*/
public abstract void makeTitle(String title);
/**
* 编写条目
* @param items
*/
public abstract void makeItems(String[] items);
/**
* 完成编写编写文档
*/
public abstract void close();
}
Director类
Director类聚合了Builder类,通过construct方法使用Builder类中的方法来构建文档
这个对象。最终传递给Director类的Builder类的实例可以是Builder类的任何子类,他们具体实现了编写文档的细节。
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.makeTitle("构建文档");
builder.makeString("升国旗");
builder.makeItems(new String[]{"奏国歌", "演讲"});
builder.close();
}
}
TextBuilder类
TextBuilder类是Builder类的子类,具体实现了Builder类中定义的编写文档的方法,还额外提供了获取文档实例的方法。
public class TextBuilder extends Builder{
private StringBuffer buffer = new StringBuffer();
@Override
public void makeString(String str) {
buffer.append("■ " + str + "\n");
}
@Override
public void makeTitle(String title) {
buffer.append("=========================\n");
buffer.append("【 " + title + " 】\n");
buffer.append("\n");
}
@Override
public void makeItems(String[] items) {
for (int i = 0; i < items.length; i++) {
buffer.append("\t" + "." + items[i] + "\n");
}
buffer.append("\n");
}
@Override
public void close() {
buffer.append("=========================\n");
}
public String getResult(){
return buffer.toString();
}
}
HtmlBuilder类
HtmlBuilder类是Builder类的子类,具体实现了Builder类中定义的编写文档的方法,还额外提供了获取文档实例的方法。
public class HtmlBuilder extends Builder{
private String filename;
private PrintWriter writer;
public HtmlBuilder(String filename) {
this.filename = filename;
}
@Override
public void makeString(String str) {
writer.println("<p>"+str+"</p>");
}
@Override
public void makeTitle(String title) {
filename = "e:\\" + filename +".html";
try {
writer = new PrintWriter(new FileWriter(filename));
} catch (IOException e) {
e.printStackTrace();
}
writer.println("<html><head><title>"+title+"</title></head><body>");
writer.println("<h1>"+title+"</h1>");
}
@Override
public void makeItems(String[] items) {
writer.println("<ul>");
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类
Main类是一个测试类,将Builder类的子类的实例对象传递到Director类的构造器中,而Director类是通过Builder类中提供的方法来构建实例,并不关心具体实现到底是哪个子类,这也就将产品的构建过程(Director)与产品本身(ConcreteBuilder)给隔离开来。
public class Main {
public static void main(String[] args) {
TextBuilder builder = new TextBuilder();
Director director = new Director(builder);
director.construct();
System.out.println(builder.getResult());
HtmlBuilder htmlBuilder = new HtmlBuilder("建造者模式");
director = new Director(htmlBuilder);
director.construct();
System.out.println("文件名为:" +htmlBuilder.getResult());
}
}
运行结果
TextBuilder
HtmlBuilder
优缺点
- 优点:
- 封装性好:产品的构建与表示是分离的。客户端不需要知道产品内部的组成细节,建造者可以对产品的创建过程细化,而不会对其他模块产生影响。
- 扩展性好:各个具体建造者是相互独立的,有利于系统的解耦。
- 缺点:维护成本高。每个建造者类只能服务与一种生产模式,如果产品本身(Builder)发生改变,那么所有的具体建造者类都要发生改变,后期维护成本还是很高的。
总结
可以发现,建造者模跟工厂模式是很类似的,都是用来创建对象。但建造者模式注重组装,而工厂模式注重创建。这两者可以结合起来使用,使用工厂模式创建零部件,使用建造者模式组装最后的产品。
建造者模式对产品的构建与表示进行了隔离,相同的构建过程可以生产不同的表示。建造者模式引入了一个非常重要的角色–指挥者(Director),他有两个主要作用:一是将客户端与产品的生产过程进行了隔离;二是控制了产品的生产过程。指挥者面向的是抽象编程,客户端只需要知道具体建造者的类型,就可以通过Director类调用相关建造方法,返回一个产品对象。