这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离。Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。一个复杂的对象有大量的组成部分,比如汽车它有车轮、方向盘、发动机、以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过程无疑会复杂,对于这种情况,为了实现在构建过程中对外部隐藏具体细节,就可以使用 Builder 模式将部件和组装过程分离,使得构建过程和部件都可以自由扩展,同时也能够将两者之间的耦合降到最低。
转载请注明出处:http://blog.csdn.net/self_study/article/details/51707029。
PS:对技术感兴趣的同鞋加群544645972一起交流。
设计模式总目录
特点
将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。Builder 模式适用的使用场景:
- 相同的方法,不同的执行顺序,产生不同的事件结果;
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
- 产品类非常复杂,或者产品类中的调用顺序不同产生不同的作用,这个时候使用建造者模式非常适合;
- 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。
在实际开发过程中,Builder 模式可以使用工厂模式或者任一其他创建型模式去生成自己的指定组件,Builder 模式也是实现 fluent interface 一种非常好的方法。
UML类图
Builder 模式的 uml 类图如下所示:
有四个角色:
- Product 产品模块
- Builder 接口或抽象类
- ConcreateBuilder 类
- Director 类
产品的相关类;
规范产品的组件,一般是由子类实现具体的组件过程,需要注意的是这个角色在实际使用过程中可以省略,最典型的就是像 AlertDialog.Builder 一样,省略 Builder 虚拟类,将 ConcreteBuilder 写成一个静态内部类;
具体的 Builder 类;
统一组装过程,同样值得注意的是,在现实开发过程中,Director 角色也经常会被省略,而直接使用一个 Builder 来进行对象的组装,这个 Builder 通常为链式调用,也就是上面提到的 fluent interface ,它的关键点是每个 setter 方法都返回自身,也就是 return this,这样就使得 setter 方法可以链式调用,最典型的仍然是 AlertDialog.Builder 类,使用这种方式不仅去除了 Director 角色,使得整个结构更加简单,也能对 Product 对象的组件过程有着更精细的控制。
据此我们可以写出 Builder 模式的通用代码:
Product.class
public class Product {
public int partB;
public int partA;
public int getPartA() {
return partA;
}
public void setPartA(int partA) {
this.partA = partA;
}
public int getPartB() {
return partB;
}
public void setPartB(int partB) {
this.partB = partB;
}
@Override
public String toString() {
return "partA : " + partA + " partB : " + partB;
}
}
产品类在此声明了两个 setter 方法,然后是 Builder 相关类:
Builder.class
public abstract class Builder {
public abstract void buildPartA(int partA);
public abstract void buildPartB(int partB);
public abstract Product build();
}
ConcreteBuilder.class
public class ConcreteBuilder extends Builder{
private Product product = new Product();
@Override
public void buildPartA(int partA) {
product.setPartA(partA);
}
@Override
public void buildPartB(int partB) {
product.setPartB(partB);
}
@Override
public Product build() {
return product;
}
}
Builder 这两个类用来封装对 Product 属性的设置,最后在 build 方法中返回设置完属性的 Product 对象,最后是 Director 角色:
Director.class
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct(int partA, int partB) {
builder.buildPartA(partA);
builder.buildPartB(partB);
}
}
封装了 Builder 对象,最后是测试程序:
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct(1, 2);
Product product = builder.build();
Log.e("shawn", product.toString());
break;
运行结果
com.android.builderpattern E/shawn: partA : 1 partB : 2
代码一目了然,这里需要提到的一点是针对不同的产品可以去构建不同的 ConcreteBuilder 类,使得一个 ConcreteBuilder 类对应一个 Product 类,这点和工厂方法模式很类似,我们后面也会介绍到他们两者之间的区别。
示例与源码
Builder 模式在实际开发中出现和使用的频率也是很高的,比如上面提到的 AlertDialog.Builder ,还比如非常有名的第三方开源框架 Universal-Image-Loader 库中的 ImageLoaderConfig ,他们都是使用的静态内部 Builder 类。
这里的 demo 也使用最简单的内部静态 Builder 类去实现,精简完之后只有 ConcreteBuilder 和 Product 角色,并且使用链式调用去实现上面提到的 fluent interface:
Computer.class
public class Computer {
private String CPU;
private String GPU;
private String memoryType;
private int memorySize;
private String storageType;
private int storageSize;
private String screenType;
private float screenSize;
private String OSType;
public static class Builder {
// Optional parameters - initialize with default values
private String CPU = "inter-i3";
private String GPU = "GTX-960";
private String memoryType = "ddr3 1666MHz";
private int memorySize = 8;//8GB
private String storageType = "hdd";
private int storageSize = 1024;//1TB
private String screenType = "IPS";
private float screenSize = 23.8f;
private String OSType = "Windows 10";
public Builder() {
}
public Builder setCPU(String CPU) {
this.CPU = CPU;
return this;
}
public Builder setGPU(String GPU) {
this.GPU = GPU;
return this;
}
public Builder setMemoryType(String memoryType) {
this.memoryType = memoryType;
return this;
}
public Builder setMemorySize(int memorySize) {
this.memorySize = memorySize;
return this;
}
public Builder setStorageType(String storageType) {
this.storageType = storageType;
return this;
}
public Builder setStorageSize(int storageSize) {
this.storageSize = storageSize;
return this;
}
public Builder setScreenType(String screenType) {
this.screenType = screenType;
return this;
}
public Builder setScreenSize(float screenSize) {
this.screenSize = screenSize;
return this;
}
public Builder setOSType(String OSType) {
this.OSType = OSType;
return this;
}
public Computer create() {
return new Computer(this);
}
}
private Computer(Builder builder) {
CPU = builder.CPU;
GPU = builder.GPU;
memoryType = builder.memoryType;
memorySize = builder.memorySize;
storageType = builder.storageType;
storageSize = builder.storageSize;
screenType = builder.screenType;
screenSize = builder.screenSize;
OSType = builder.OSType;
}
}
Computer 为产品类,它有一个 Builder 的静态内部类用于设置相关属性,测试代码:
Computer computer = new Computer.Builder()
.setCPU("inter-skylake-i7")
.setGPU("GTX-Titan")
.setMemoryType("ddr4-2133MHz")
.setMemorySize(16)
.setStorageType("ssd")
.setStorageSize(512)
.setScreenType("IPS")
.setScreenSize(28)
.setOSType("Ubuntu/Window10")
.create();
这里需要提到的关键点是关于相关属性的默认值问题:
- 对于必要的属性值,无法给出其默认值的最好是通过 Builder 类的构造函数传入,比如 AlertDialog.Builder 类的 Context,这样也能防止使用时的疏忽;
- 对于非必要属性来说,最好是为其生成一个默认的属性值,这样使用者只用设置需要更改的属性即可;
- 每个 setter 函数都加上 return this 用来实现优美的 fluent interface 设计。
总结
Builder 模式在 Android 开发中也很常用,通常作为配置类的构建器将配置的构建和表示分离开来,同时也将配置从目标类中隔离开来,避免了过多的 setter 方法。Builder 模式比较常见的实现形式是通过调用链实现,这样的方式也会使得代码更加简洁和易懂,而且同时也可以避免了目标类被过多的接口“污染”。
Builder 模式的优点:
- 将一个复杂对象的创建过程封装起来,使得客户端不必知道产品内部组成的细节;
- 允许对象通过多个步骤来创建,并且可以改变过程和选择需要的过程;
- 产品的实现可以被替换,因为客户端只看到一个抽象的接口;
- 创建者独立,容易扩展。
Builder 模式缺点:
- 会产生多余的 Builder 对象以及 Director 对象,消耗内存;
- 与工厂模式相比,采用 Builder 模式创建对象的客户,需要具备更多的领域知识。
Builder 模式 VS 工厂方法模式
Builder 模式和工厂方法模式都是属于创建型模式,他们有一些共同点:这两种设计模式的都将一个产品类对象的创建过程封装起来,让客户端从具体产品类的生成中解耦,不必了解产品类构造的细节。但是其实他们两种设计模式还是有很多不同点:
- Builder 模式允许对象的创建通过多个步骤来创建,而且可以改变这个过程,也可以选择需要改变的属性;工厂方法模式不一样,它只有一个步骤,也就无法改变这个过程,更加无法选择性改变属性了;
- Builder 模式的目的是将复杂对象的构建和它的表示分离;而工厂方法模式则是定义一个创建对象的接口,由子类决定要实例化的类是哪一个,将实例化推迟到子类;
- 最明显的当然还是代码的差异,Builder 模式中客户端可以调用 set 方法,而工厂方法模式只能调用工厂类提供的相关方法。
其次是 uml 类图的差异:
注:Director 类和 Builder 虚拟类可以被精简。
uml 类图的相似性还是很高的,所以通常我们会根据实际表现和用途来区别 Buidler 模式和工厂方法模式(这点和装饰者模式与保护代理模式的区别类似,要从实际表现与使用的目的区别)。
源码下载
https://github.com/zhaozepeng/Design-Patterns/tree/master/BuilderPattern