java/android 设计模式学习笔记(10)---建造者模式
Posted Shawn_Dut
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java/android 设计模式学习笔记(10)---建造者模式相关的知识,希望对你有一定的参考价值。
这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离。Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。一个复杂的对象有大量的组成部分,比如汽车它有车轮、方向盘、发动机、以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过程无疑会复杂,对于这种情况,为了实现在构建过程中对外部隐藏具体细节,就可以使用 Builder 模式将部件和组装过程分离,使得构建过程和部件都可以自由扩展,同时也能够将两者之间的耦合降到最低。
转载请注明出处:http://blog.csdn.net/self_study/article/details/51707029。
PS:对技术感兴趣的同鞋加群544645972一起交流。
设计模式总目录
特点
将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。Builder 模式适用的使用场景:
- 相同的方法,不同的执行顺序,产生不同的事件结果;
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
- 产品类非常复杂,或者产品类中的调用顺序不同产生不同的作用,这个时候使用建造者模式非常适合;
- 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。
UML类图
Builder 模式的 uml 类图如下所示:
有四个角色:
- Product 产品模块
- 产品的相关类;
- Builder 接口或抽象类
- 规范产品的组件,一般是由子类实现具体的组件过程,需要注意的是这个角色在实际使用过程中可以省略,最典型的就是像 AlertDialog.Builder 一样,省略 Builder 虚拟类,将 ConcreteBuilder 写成一个静态内部类;
- ConcreateBuilder 类
- 具体的 Builder 类;
- Director 类
- 统一组装过程,同样值得注意的是,在现实开发过程中,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 对象以及 Director 对象,消耗内存;
- 与工厂模式相比,采用 Builder 模式创建对象的客户,需要具备更多的领域知识。
Builder 模式 VS 工厂方法模式
Builder 模式和工厂方法模式都是属于创建型模式,他们有一些共同点:这两种设计模式的都将一个产品类对象的创建过程封装起来,让客户端从具体产品类的生成中解耦,不必了解产品类构造的细节。但是其实他们两种设计模式还是有很多不同点:
- Builder 模式允许对象的创建通过多个步骤来创建,而且可以改变这个过程,也可以选择需要改变的属性;工厂方法模式不一样,它只有一个步骤,也就无法改变这个过程,更加无法选择性改变属性了;
- Builder 模式的目的是将复杂对象的构建和它的表示分离;而工厂方法模式则是定义一个创建对象的接口,由子类决定要实例化的类是哪一个,将实例化推迟到子类;
- 最明显的当然还是代码的差异,Builder 模式中客户端可以调用 set 方法,而工厂方法模式只能调用工厂类提供的相关方法。
Builder 模式 uml 类图:
注:Director 类和 Builder 虚拟类可以被精简。
工厂方法模式 uml 类图:
uml 类图的相似性还是很高的,所以通常我们会根据实际表现和用途来区别 Buidler 模式和工厂方法模式(这点和 装饰者模式与 保护代理模式的区别类似,要从实际表现与使用的目的区别)。
创建型模式 Rules of thumb
有些时候创建型模式是可以重叠使用的,有一些抽象工厂模式和原型模式都可以使用的场景,这个时候使用任一设计模式都是合理的;在其他情况下,他们各自作为彼此的补充:抽象工厂模式可能会使用一些原型类来克隆并且返回产品对象。
抽象工厂模式,建造者模式和原型模式都能使用单例模式来实现他们自己;抽象工厂模式经常也是通过工厂方法模式实现的,但是他们都能够使用原型模式来实现;
通常情况下,设计模式刚开始会使用工厂方法模式(结构清晰,更容易定制化,子类的数量爆炸),如果设计者发现需要更多的灵活性时,就会慢慢地发展为抽象工厂模式,原型模式或者建造者模式(结构更加复杂,使用灵活);
原型模式并不一定需要继承,但是它确实需要一个初始化的操作,工厂方法模式一定需要继承,但是不一定需要初始化操作;
使用装饰者模式或者组合模式的情况通常也可以使用原型模式来获得益处;
单例模式中,只要将构造方法的访问权限设置为 private 型,就可以实现单例。但是原型模式的 clone 方法直接无视构造方法的权限来生成新的对象,所以,单例模式与原型模式是冲突的,在使用时要特别注意。
源码下载
https://github.com/zhaozepeng/Design-Patterns/tree/master/BuilderPattern
引用
http://blog.csdn.net/jason0539/article/details/44992733
https://en.wikipedia.org/wiki/Builder_pattern
以上是关于java/android 设计模式学习笔记(10)---建造者模式的主要内容,如果未能解决你的问题,请参考以下文章