定义
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
如果一个类的构造非常复杂,并且如果是由很多部件组合而成,而且整体的组装过程比较复杂的话,需要用到构建者。它的使用我认为有两种优势
- 对复杂的对象进行动态配置,根据实际场景选择对象的部件
- 把对象的复杂的封装组装对使用者透明(这个遇到得比较少)
构成类的部件,可以是多个一样,也可以不一样的,这个根据业务情况灵活选择
我们可以在现实生活中找到这样的模型
比如,要造一台计算机主机,需要CPU、内存卡、显卡、电源等。组装过程也相对复杂,比如一些接线的处理
比如,要造一辆汽车,需要底盘、方向盘、发动机、座椅、轮子等等
比如,要造航空母舰,需要甲板、起飞装置、机库、动力系统、武器库等
这些模型,都有一个共同点,就是主体由多个部分组成,并且主体的创建过程很复杂,有很多可选择的配置,丰富的组装细节
构建者模式的应用,就是把这个复杂的创建过程分离出来单独维护
现在拿组装电脑的案例来理解

我们只关心需要使用哪些部件,而并不关心组装过程。我们的角色是使用者,组装电脑的人是指挥者,他们怎么组装,我们并不关心。使用者选好配置后,交给指挥者,然后输出一台符合我们预期的电脑产品
简单设计
构建者模式主要有两种形态
有抽象建造者
这个形态比较复杂,可以用来创建不同的复杂的产品,把产品的部件细节放在具体建造者中实现。然后由指挥者完成复杂的组装
这个实现和上面的区别在于,对 Builder 在进行了一次抽象,并增加了一个指挥者。抽象类声明各个部分的创建,不涉及具体的对象的部件的创建。具体的创建交给它的子类具体建造者来实现
-
抽象建造者 Abstract Builder
声明创建对象需要哪些部件
-
具体建造者 Concrete Builder
定义明确的部件
-
产品 Product
目标产品,由多个部件组成
-
指挥者 Director
指挥对象各部件的构建次序,通过指挥者可以使用和构建分离
可以得到这样的类图

使用者调用 Director 的 construct 方法来完整对象的组装,这个方法内指挥者对象各部件的组装次序
public class Director {
private AbstractBuilder builder;
public Director(AbstractBuilder builder) {
this.builder = builder;
}
public Product construct() {
builder.buildA();
builder.buildB();
builder.buildC();
return builder.getResult();
}
}
使用者直接向指挥者拿最终的产品,整个有点像抽象工厂模式
构建者模式返回一个完整产品,抽象工厂返回一系列产品。构建者模式侧重一步步构造和组建复杂对象,把结果返回。工厂模式侧重生产不同类型的部件
每种具体的 Builder 都是独立的,到时候需要构建新产品,只需要扩展新的 Builder,实现开闭原则
适用:
- 产品由多个部件组成
- 部件中依赖关系复杂
- 创建过程中需要用到系统的某些对象
无抽象建造者
这个模式的基本角色只有俩:
-
建造者 Builder
定义部件,并提供方法把所有部件组合成产品
-
产品 Product
我们的目标产品,由多个部件组成
这个形态,更侧重于对产品的部件进行按需配置。每个产品需要什么样的部件,交给外部使用者 Client 去选择。这个形态承担了大部分开源库工具类的使用配置,我们的实际应用中大量使用了这个方式,因为简单够用
最后的组装过程发生在 build 方法中,该方法内确定了各部件的组装次序
可以有这样的类图

适用:
- 产品由多个部件组成
- 部件中依赖关系简单
- 创建过程不需要或者较少使用到系统的其他对象
应用实例
构建者模式用得比较多的有两种场景
- 把复杂对象的构建和实现分离,相同的构建过程生产出不同的产品。我们不需要关系构建的过程,只需要知道我们要什么部分。比如对话框的创建
- 方法多态的改良,可选参数多的场景,比如一个对象的创建有多个可选参数,而且参数还会继续扩展。或者某个执行方法有多个可选参数。应用构建者模式可以让代码简洁,易与扩展与编写。我们举个方法多态的改良例子
对话框的创建
一个对话框由多个部件组成,比如标题栏、内容、确定按钮、取消按钮等等。有的页面需要标题栏,有的页面不需要,有的页面需要确定按钮,有的不需要,这些都通过 Builder 来构建这个复杂的对话框
CommonAlertDialogFragment builder = new CommonAlertDialogFragment.Builder(getActivity())
.setMessage(xxxx)
.setCancelable(true)
.setNegativeButtonText(xxx, null)
.setPositiveButtonText(xxx, null)
.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss() {
...
}
});
CommonAlertDialogFragment dialog = builder.create()
方法多态的改良
实际应用中,也可以使用构建者来解决一个多参数构造类的问题,对一些参数设置默认值,按需配置,可以帮助类减少方法重载导致方法数量膨胀的问题。假设我们要用客户端下载一个文件,需要配置一些下载参数
-
必选项
下载的 url 地址
下载后的文件的保存地址
-
可选项
是否支持多线程,默认不支持
是否支持文件覆盖,默认可以
下载失败重试次数,默认10次
如果直接用方法多态的方式,如果想要尽可能地覆盖这些条件的组合所有的情况,需要很多的重载方法
void downloadFile(String url, String savePath);
void downloadFile(String url, String savePath, boolean multiThread);
void downlaodFile(String url, String savePath, boolean multiThread, boolean overwrite);
void downloadFile(String url, String savePath, boolean multiThread, boolean overwrite, int retryNum);
void downloadFile(String url, String savePath, int retryNum);
...
如果未来又增加一个新的条件,方法数量又会有膨胀出好几个;而删除一个条件,则会干扰到多个方法
所以,我们用构建者模式来改善它
public static class Builder {
private String url;
private String savePath;
private int retryNum = 0;
private boolean multiThread = false;
private boolean overwrite = true;
public Builder(String url, String savePath) {
this.url = url;
this.savePath = savePath;
}
public Builder addRetryNum(int retryNum) {
this.retryNum = retryNum;
return this;
}
public Builder addMultiThread(boolean multiThread) {
this.multiThread = multiThread;
return this;
}
public Builder addOverwrite(boolean overwrite) {
this.overwrite = overwrite;
return this;
}
public DownloadParams build() {
return new DownloadParams(url, savePath, retryNum, callBack, multiThread, overwrite, sync);
}
}
然后方法缩减为只有一个
void download(DownloadParams params);
这样用到下载的地方,进行按需配置,不用担心方法数过度膨胀难以更新和维护的问题
开源库使用举例
这些开源库,大都使用构建者模式来满足丰富的配置选项
OKHttp 的创建
OKHttp 的创建,OkHttp 作为 HTTP 请求的客户端,需要配置超时时间、是否允许重定向处理、是否允许连接失败重试等
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.MILLISECONDS)
.readTimeout(60, TimeUnit.MILLISECONDS)
.writeTimeout(60, TimeUnit.MILLISECONDS)
.followRedirects(true);
.followSslRedirects(true);
.retryOnConnectionFailure(true);
OkHttpClient okHttpClient = builder.build();
通过 Builder 来存储 OkHttpClient 的配置,最后使用这些可选配置完成 OkHttp 的创建
ImageLoader 的创建
ImageLoader 的创建,需要配置线程池核心线程大小、磁盘缓存策略、内存缓存的大小
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(MeiPaiApplication.getApplication())
.memoryCacheSizePercentage(13)
.threadPoolSize(Runtime.getRuntime().availableProcessors())
.denyCacheImageMultipleSizesInMemory()
.diskCache(new UnlimitedDiscCache(mImageCacheDir, null, new Md5FileNameGenerator()))
.tasksProcessingOrder(QueueProcessingType.LIFO)
.build();
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(config);
GsonBuilder 的创建
作为部件,不一定非得每个部件都不一样,有的时候,需要过个相同类型的部件构成。比如我们使用 Gson 库来解析 Json 字符串的时候,需要一个 GsonBuilder,它就可以传入多个对象解析器来处理对象的解析
GsonBuilder buider = new GsonBuilder().
.registerTypeAdapter(MediaBean.class, new MediaBeanDeserializer())
.registerTypeAdapter(UserBean.class, new UserBeanDeserializer())
.registerTypeAdapter(LiveBean.class, new LiveBeanDeserializer());
Gson gson = builder.create();
网友评论