定义
将一个复杂对象的构建与它的表示进行分离,使得同样的构建过程可以创建不同的表示。
对象创建型的设计模式
Builder模式是一步步创建一个复杂对象的创建模式,它允许用户在不知道内部构建细节的情况下,可以更精细的控制对象的构造流程。使得构造过程和部件都可以自由扩展,两者间的耦合也降到最低。
关键点
- 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- 创建的复杂的对象的方法应该独立于该对象的组成部分以及其装配的方法
- builder模式多处理是用来的聚合的观念,而不是继承的观念
- 不同的product类的组成的过程中,不能进行组合和替换
使用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果时
- 多个部件都可以装配到一个对象中,但是产生的运行结果又不相同时
- 产品类非常复杂,或产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常适合
- 当初始化一个对象特别复杂,如参数多。且很多参数都有默认值时
场景例子说明
假设要组装一台电脑,简化为构建主机、设置操作系统、设置显示器三部分。
public class Computer {
private String mBoard;//主板
private String mDisplay;//显示器
private String mOS;//操作系统
}
我们可以在构造方法中进行定义,用什么样的主板、显示器、操作系统,如下:
构造器模式
public Computer(String mBoard, String mDisplay, String mOS) {
this.mBoard = mBoard;
this.mDisplay = mDisplay;
this.mOS = mOS;
}
但是当我们属性越来越多的时候,构造方法的方式显得不实用了。代码可读性变差,难以维护,调用者调用复杂等一系列缺点。
于是我们可以用Setter、Getter
方法(JavaBean模式)。然而这种方式也存在或多或少的问题,比如当有大量的属性存在后,可能在前面已经set
了主板,却再后面又重新修改了主板,这样很容易造成不安全的情况。
//属性越多,调用者重复越多
computer.setBoard("");
computer.setDisplay("");
computer.setOS("");
.....
//修改已经设置好了的某个属性
computer.setBoard("修改后的属性");
这时候可以考虑采用Builder模式。
经典的Builder模式
- 首先,我们建立一个计算机抽象类,代表UML类图中的Product角色
***
Product:被构造的复杂对象---->Computer
***
//计算机抽象类,即Product角色
public abstract class Computer {
protected String mBoard;//主机
protected String mDisplay;//显示器
protected String mOS;//操作系统
private Computer() {
}
/**
* 设置主机
*
* @return
*/
public void setmBoard(String mBoard) {
this.mBoard = mBoard;
}
/**
* 设置显示器
*
* @param mDisplay
*/
public void setmDisplay(String mDisplay) {
this.mDisplay = mDisplay;
}
/**
* 设置操作系统
*
* @param mOS
*/
public void setmOS(String mOS) {
this.mOS = mOS;
}
@Override
public String toString() {
return "Computer [mBorder=" + mBoard +
", mDisplay=" + mDisplay + ", mOs=" + mOS + "]";
}
}
- 对计算机类进行具体实现
//具体的Computer类
public class MateBook extends Computer {
protected MateBook() {
}
@Override
public void setmOS() {
mOS = "HUAWEI MateBook Android 7.0";
}
}
- 构建Builder类
***
Builder:抽象接口---->Builder
Builder类负责的复杂对象的组成部分的装载
***
//抽象Builder类
public abstract class Builder {
//设置主板、显示器、操作系统
public abstract void buildBoard(String board);
public abstract void buildDisplay(String display);
public abstract void buildOS();
//创建Computer
public abstract Computer create();
}
- 具体的Builder类,即其实现
***
BuilderImpl:抽象接口的具体实现---->MateBookBuilder
***
//具体的Builder类
public class MateBookBuilder extends Builder {
private Computer mComputer = new MateBook();
@Override
public void buildBoard(String board) {
mComputer.setmBoard(board);
}
@Override
public void buildDisplay(String display) {
mComputer.setmDisplay(display);
}
@Override
public void buildOS() {
mComputer.setmOS();
}
@Override
public Computer create() {
return mComputer;
}
}
- Director类,负责构造Computer
Director类负责了复杂对象的装配的过程
***
Director:接口的构造者和使用者 ---->Goods
***
//Director类,复杂构造Computer
public class Director {
Builder mBuilder = null;
public Director(Builder builder) {
mBuilder = builder;
}
/**
* 构建对象
*
* @param board
* @param display
*/
public void construct(String board, String display) {
mBuilder.buildBoard(board);
mBuilder.buildDisplay(display);
mBuilder.buildOS();
}
}
- 测试
public class ComputerTest {
public static void main(String[] args) {
//创建构建器
Builder builder = new MateBookBuilder();
//Director
Director director = new Director(builder);
//封装构建过程
director.construct("XXX主板", "XXX显示器");
//构建计算机,输出信息
System.out.println(builder.create().toString());
}
}
result:
Computer [mBorder=XXX主板, mDisplay=XXX显示器, mOs=HUAWEI MateBook Android 7.0]
上述例子中,通过具体的MateBookBuilder来构建MateBook对象,而Director封装了构建复杂产品对象的过程,对外隐藏构建细节。Builder与Director一起将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的对象。
Builder模式需要注意的地方
builder模式多处理是用来的聚合的观念,而不是继承的观念
这部分的理解非常重要
builder模式主要是用来创建复杂的对象,从而实现对对象的创建过程和表示层实现分离。
我们需要考虑下builder模式到底真正的含义是什么?
参考引文:
Android Builder模式
Builder模式的误区:将复杂对象的构建进行封装,就是Builder模式了吗?
变种Builder模式
变种Builder模式在现实Android开发中会经常用到。现实开发中,Director角色会经常被省略。而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,它的关键点是每个setter方法都返回自身,即return this
。
调用如下:
new TestBuilder().setA("A").setB("B").create()
变种Builder模式和经典Builder模式区别
- 链式调用,结构清晰;
- 属性设置后,不会被修改,安全性较高;(也是变种builder基本不使用单例理由之一)
- 因为变种builder模式主要是以静态内部类实现,需求改变后,只需要替换新的静态内部类,原来的静态内部类可以原封不动,拓展性和维护较好
变种Builder的常用写法,Android中常见的链式调用
//变种Builder。Director角色会被省略
public class BMateBookBuilder {
private Computer mComputer;
public BMateBookBuilder() {
mComputer = new MateBook();
}
public BMateBookBuilder(Computer computer) {
mComputer = computer;
}
public BMateBookBuilder buildBoard(String board) {
mComputer.setmBoard(board);
return this;
}
public BMateBookBuilder buildDisplay(String display) {
mComputer.setmDisplay(display);
return this;
}
public BMateBookBuilder buildOS() {
mComputer.setmOS();
return this;
}
public Computer create() {
return mComputer;
}
}
//调用的时候如下:
new BMateBookBuilder()
.buildBoard("TTT主板")
.buildDisplay("TTT显示器")
.buildOS()
.create();
new BMateBookBuilder(new MateBook())
.buildBoard("HHH主板")
.buildDisplay("HHH显示器")
.buildOS()
.create();
//输出toString()
Computer [mBorder=TTT主板, mDisplay=TTT显示器, mOs=HUAWEI MateBook Android 7.0]
Computer [mBorder=HHH主板, mDisplay=HHH显示器, mOs=HUAWEI MateBook Android 7.0]
在Android中,Builder链式调用通常采用内部静态类构建Builder
public class BMateBook extends Computer {
private BMateBook(Builder builder) {
this.mBoard = builder.mBoard;
this.mDisplay = builder.mDisplay;
setmOS();
}
@Override
public void setmOS() {
mOS = "HUAWEI MateBook Android 7.0";
}
public static class Builder {
private String mBoard;
private String mDisplay;
public Builder setBoard(String board) {
this.mBoard = board;
return this;
}
public Builder setDisplay(String display) {
this.mDisplay = display;
return this;
}
public BMateBook build() {
return new BMateBook(this);
}
}
}
//调用方式:
new BMateBook.Builder()
.setBoard("NNN主板")
.setDisplay("NNN显示器")
.build()
//输出toString()
Computer [mBorder=NNN主板, mDisplay=NNN显示器, mOs=HUAWEI MateBook Android 7.0]
- 定义一个静态内部类Builder,内部的成员变量和外部类一样
- Builder类通过一系列的方法用于成员变量的赋值,并返回当前对象本身(this)
- Builder类提供一个build方法或者create方法用于创建对应的外部类,该方法内部调用了外部类的一个私有构造函数,该构造函数的参数就是内部类Builder
- 外部类提供一个私有构造函数供内部类调用,在该构造函数中完成成员变量的赋值,取值为Builder对象中对应的值
如何保证Builder模式下线程安全?
public class AQ_BMateBook {
//都设置为final类型,不可变对象,以保证线程的安全
private final String board;
private final String display;
private final String os;
//传入builder对象,通过builder对象来初始化参数
private AQ_BMateBook(Builder builder) {
this.board = builder.mBoard;
this.display = builder.mDisplay;
this.os = "HUAWEI MateBook Android 7.0";
}
@Override
public String toString() {
return "Computer [mBorder=" + board +
", mDisplay=" + display + ", mOs=" + os + "]";
}
public static class Builder {
private String mBoard;
private String mDisplay;
private String mOS;
public Builder setBoard(String board) {
this.mBoard = board;
return this;
}
public Builder setDisplay(String display) {
this.mDisplay = display;
return this;
}
public Builder setOS(String os) {
this.mOS = os;
return this;
}
public AQ_BMateBook create() {
return new AQ_BMateBook(this);
}
}
}
对于线程安全这部分了解的不够明白,如有错误望指正。
Builder模式的优缺点
优点:
- 良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节
- 建造者独立,容易扩展
缺点:
- 会产生多余的Builder对象以及Director对象,消耗内存
本文引用了很多内容,自身对Builder模式的理解还不够深刻,若有不正确的地方,望指正。
引用:
《Android源码设计模式解析与实战》
Android中建造者(builder)模式
Android Builder模式
Builder模式的误区:将复杂对象的构建进行封装,就是Builder模式了吗?
Android中的构建者(Builder)模式