Builder模式,也叫建造者模式,它将一个复杂对象的构建与它的表示分离,使得同样的创建过程可以创建不同的表示。它是一步一步创建一个复杂对象的创建型模式,允许用户在不知道内部构建细节的情况下,可以更精细的控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来。通常一个复杂对象有很多组成部分,就像一辆汽车有很多零件,把这些零件全部装配起来是一个庞大的工程,对于这种情况,为了在构建过程中对外部隐藏实现细节,就可以使用Builder模式将部件与组装分离,使得构建过程和部件都可以自由扩展,两者之间的耦合也降到最低。看完这一段话,其实不太好理解,其实这个设计模式我们都已经接触过,我们熟悉的AlertDialog.Builder就是Builder模式在安卓源码中的典型应用。通过Builder来设置Dialog的标题,Message,确定取消按钮等等,将Dialog的构造与表示分离。
Builder模式的使用场景:
(1)相同的方法,不同的执行顺序,产生不同的事件结果时。
(2)多个部件或零件,都可以装配到一个对象中,但是产生的运 行结果又不相同时。
(3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用。
(4)当初始化一个对象特别复杂,如参数多,且很多参数都有默认值。
一个标准的Builder模式一般有下面几个部分:
Product产品类
Builder:抽象Builder类,规范产品的组建,一般由子类实现具体的组建过程
ConcreteBuilder:具体的Builder类
Director:统一组装过程
下面我们来看一个典型的Builder模式的工作流程。我们以电脑为例,简单分为三个部分,品牌,大小和操作系统。我们先看一下产品类Computer,和普通的实体类并没有什么区别,一般是一些复杂对象。实际开发中也经常定义出一个抽象产品类,用来继承实现具体的产品类。
/**
* 产品类
*/
public class Computer {
private String brand;// 品牌
private String size;// 大小
private String os;// 操作系统
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
public String toString() {
return "Computer [brand=" + brand + ", size=" + size + ", os=" + os
+ "]";
}
}
接着定义一个抽象建造者(Builder)类,当然也可以是接口。它的作用是规范产品类各个部分的建造,同时提供一个方法来返回产品实例。这里的Builder类提供了设置品牌,设置大小,设置操作系统四个抽象方法,还有返回Computer实例的create方法。
/**
* 抽象Builder类
*/
public abstract class Builder {
// 设置品牌
public abstract void setBrand(String brand);
// 设置大小
public abstract void setSize(String size);
// 设置操作系统
public abstract void setOs(String os);
//创建Computer
public abstract Computer create();
}
我们通过继承Builder类,实现一个具体的ComputerBuilder类,这个具体的建造者类是直接和系统进行交流的,系统调用它来直接创建产品实例。
/**
* 具体的Builder类
*/
public class ComputerBuilder extends Builder {
private Computer mComputer = new Computer();
public void setBrand(String brand) {
mComputer.setBrand(brand);
}
public void setSize(String size) {
mComputer.setSize(size);
}
public void setOs(String os) {
mComputer.setOs(os);
}
public Computer create() {
return mComputer;
}
}
最后,通过导演(Director)类,调用具体建造者类来创建产品。产品的具体信息并不是导演类所有,而是导演类所持有的具体建造者类所有。
/**
* Director类,负责构造Computer
*/
public class Director {
private Builder mBuilder = null;
public Director(Builder builder) {
mBuilder = builder;
}
public void construct(String brand, String size, String os) {
mBuilder.setBrand(brand);
mBuilder.setSize(size);
mBuilder.setOs(os);
}
}
我们来测试一下,
public static void main(String[] a){
Builder builder=new ComputerBuilder();
Director director=new Director(builder);
director.construct("Intel","15.6","Window 10");
System.out.println(builder.create().toString());
}
输出结果是:Computer [brand=Intel, size=15.6, os=Window 10]
这样,通过具体的Builder对象构建产品类,对象,Director类封装了构造复杂对象的过程,对用户隐藏细节。Builder和Director一起将一个复杂对象的构建和表示分离。当然在现实开发中,我们并不会完全按照上面的形式去使用Builder模式,有时候我们会省略掉Director,直接在产品类中写一个静态的内部Builder类,并让他的setter方法返回this以实现链式调用。什么是链式调用呢,想想最简单的AlertDialog.Builder的使用方法,
new AlertDialog.Builder(this).setTitle("是否退出").setMessage("请选择").setIcon(R.drawable.ic_launcher).create().show();
这样可以直接在后面追加调用方法就是链式调用,一定程度上简化了编码,提高工作效率。下面还是上面电脑的例子,用内部静态Builder类的模式看一下建造者模式的应用。
/**
* Created by sun on 2016/1/26.
* 静态内部Builder类
*/
public class Computer2 {
//产品组成部分
private String brand;
private String size;
private String os;
//传入Builder对象,进行组成部分的构造
private Computer2(Builder builder) {
brand = builder.mBrand;
size = builder.mSize;
os = builder.mOs;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
@Override
public String toString() {
return "Computer2{" +
"brand='" + brand + '\'' +
", size='" + size + '\'' +
", os='" + os + '\'' +
'}';
}
//静态内部的Builder类
public static class Builder {
private String mBrand;
private String mSize;
private String mOs;
//Builder构造函数,创建Builder类的时候传入一些公共的,或者默认的值
public Builder(String brand) {
mBrand = brand;
}
/**
*提供组成部分的建造方法
* 返回this实现链式调用
*/
public Builder setSize(String size) {
mSize = size;
return this;
}
public Builder setOS(String os) {
mOs = os;
return this;
}
//返回产品实例
public Computer2 create(){
return new Computer2(this);
}
}
}
首先来测试一下这种写法,
public static void main(String[] a){
Computer2 computer2=new Computer2.Builder("MacBook").setSize("13.3").setOS("OS").create();
System.out.println(computer2.toString());
}
打印结果:Computer2{brand='MacBook', size='13.3', os='OS'}
。
这种写法省略了Director类,看起来更容易理解一点。在产品类中嵌套一个静态Builder类,我们在产品类的构造函数中传入了Builder类,这样在Builder类中构建好的组成部分就传给了产品类。尽管这样看起来是增加了代码量,但是当产品类组成部分很多,即参数很多的时候,我们可能需要提供不止一种构造函数供外界使用,不同的构造函数在用户看来并不是那么容易看出来区别在哪里,代码可读性很差。而使用Builder模式的话用户就可以很容易的去创建自己需要的不同形式的产品。反之,当参数不多,只需要一个构造器就可以满足用户使用的时候,Builder模式反而是冗余的。
根据自己的理解Builder模式可以有多种实现方式,万变不离其宗,思想是不会变化的。当我们构建对象时,参数很多,且部分参数有默认值,并不是所有参数都是必须需要的,不同的参数组合可以有不同的对象,或者当需要用多种构造函数来构建不同对象的时候,都可以考虑使用Builder模式,并且要知道Builder模式是建立在成倍增加代码量的基础上的,所以,总会有得不偿失的时候。