建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
可以用来简化复杂对象的构造过程。
对比现实世界,类似于:
复杂对象一般具有这些特点:
建造者模式,把复杂的对象创建过程进行剥离,尤其是内部部件的配置,进行独立变化和维护。
建造者模式,一般有两种实现。
有抽象建造者会比较复杂。对建造者进一步抽象,意味着可以有不同的建造者实现。
主要角色有:
类图如下:
产品类,由多个部分组成,不同部分的组合可以视为一个新的产品:
public class Product {
private String part1;
private String part2;
private String part3;
public void setPart1(String part1) {
this.part1 = part1;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public void setPart3(String part3) {
this.part3 = part3;
}
@Override
public String toString() {
return String.format("产品:[part1=%s, part2=%s, part3=%s]", part1, part2, part3);
}
}
抽象构建者:
public interface IBuilder {
void build1();
void build2();
void build3();
Product getResult();
}
具体构建者 A:
public class BuilderA implements IBuilder {
private Product product;
public BuilderA() {
product = new Product();
}
public void build1() {
product.setPart1("A1");
}
public void build2() {
product.setPart2("A2");
}
public void build3() {
product.setPart3("A3");
}
public Product getResult() {
return product;
}
}
指挥者,指挥构建者生成产品:
public class Director {
private IBuilder builder;
public Director(IBuilder builder) {
this.builder = builder;
}
public Product construct() {
builder.build1();
builder.build2();
builder.build3();
return builder.getResult();
}
}
调用的地方如下:
public class TestBuilder {
public static void main(String[] args) {
// 生成 A 产品
IBuilder builder = new BuilderA();
Director director = new Director(builder);
Product product = director.construct();
System.out.println(product.toString());
// 生成 B 产品
builder = new BuilderB();
director = new Director(builder);
product = director.construct();
System.out.println(product.toString());
}
}
无抽象建造者的建造者模式,其实是有抽象建造者的一个退化。
比如只生产一种产品,就没有必要对建造者进行抽象。
无抽象建造者模式很简单,基本角色有:
对比有抽象建造者的建造者模式,这里的 Builder 其实承担了指挥者的角色。
这个模式,可以对产品的部件进行灵活的配置。而且可以使用点语法和链式编程,代码结构清晰。
具体怎样的配置交给外部的使用者进行选择。
大量的开源框架使用 Builder 来进行配置。
建造者模式适用于复杂对象的构造,两种不同建造者有自己的适用范围。
有抽象建造者的:
产品由多个部件组成,不同部件可以更换,不同部件的配置和更换为新的产品。
比如电脑,同一个品牌的电脑,不同的 CPU 配置和内存配置,可以视为一个新的产品。
部件之间存在复杂的依赖关系。
创建过程中还依赖到外部环境的对象。
一般由指挥者去依赖外部环境,然后把相关的对象交给建造者去处理。
无抽象建造者的:
在这些应用场景会非常有效:
一个对话框由多个部件组成,比如标题栏、内容、确定按钮、取消按钮等等。有的页面需要标题栏,有的页面不需要,有的页面需要确定按钮,有的不需要。
这些都通过 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()
在方法参数类型较多的时候,我们会使用方法多态,但这样会造成构造方法数量膨胀。
假设我们现在要设计一个下载器,有这么一些配置:
如果直接用方法多态的方式,如果想要尽可能地覆盖这些条件的组合所有的情况,需要很多的重载方法,于是有这样的代码:
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);
使用的地方按需配置。
Locale 有一个通过建造者创建的方式
Locale aLocale = new Builder()
.setLanguage("sr")
.setScript("Latn")
.setRegion("RS")
.build();
String 是不可变字符串,每次操作都会创建新的字符串。
StringBuilder 对其进行了优化,可以通过 append 操作进行字符串拼接,支持非 String 的基本数据类型。
String str = (new StringBuilder())
.append(1)
.append(1.2f)
.append("Hello World!")
.toString();
Gson 是 Google 提供的 Gson 解析框架。
Gson 可以通过 GsonBuilder 注册一些自定义的解析器,用来对 Bean 的反射做定制化处理。
GsonBuilder buider = new GsonBuilder().
.registerTypeAdapter(MediaBean.class, new MediaBeanDeserializer())
.registerTypeAdapter(UserBean.class, new UserBeanDeserializer())
.registerTypeAdapter(LiveBean.class, new LiveBeanDeserializer());
Gson gson = builder.create();
OkHttp 是 Square 公司开源的 Http 客户端,用来进行网络请求,已经支持到 Http/2。
使用这个客户端,需要配置超时时间,是否重定处理,是否允许连接失败重试等。
这里就是使用建造者模式来完成 OkHttp 的配置和实例化的:
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();