介绍
建造模式是对象的创建模式。建造模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。
备注:建造模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的零件解耦,使得构建过程和零件的表示隔离开来。
模式角色
建造模式有以下角色:
- Builder(抽象建造者),给出一个抽象接口,以规范产品对象的各个组成成分的建造。模式中直接创建产品对象的是ConcreteBuilder(具体建造者)。ConcreteBuilder必须实现这个接口所要求的两种方法:一种是建造方法,另一种是结果返还方法。一般来说,产品所包含的零件数目与建造方法的数目相等。换言之,有多少零件,就有多少相应的建造方法。
- ConcreteBuilder(具体建造者),担任这个角色的是与应用程序紧密相关的一些类,它们在应用程序调用下创建产品的实例。这个角色要完成的任务包括:①Builder(抽象建造者)所声明的接口,给出一步一步地完成创建产品实例的操作。②在建造过程完成后,提供产品的实例。
- Director(导演者、监工),担任这个角色的类调用ConcreteBuilder(具体建造者)以创建产品对象。应当指出的是,Director角色并没有Product(产品)类的具体知识,真正拥有Product(产品)类的具体知识的是ConcreteBuilder(具体建造者)。
- Product(产品),便是建造中的复杂对象。
模式结构图
模式实现
模式实现,下面通过伪代码去实现该模式:
/**
* Builder
*/
public abstract class Builder {
/**
* 产品零件建造方法
*/
public abstract void buildPart1();
/**
* 产品零件建造方法
*/
public abstract void buildPart2();
/**
* 产品返回方法,实际使用可以使用标记接口来代替返回类型
* 可根据实际情况,产品返回方法可在Builder角色中去掉,而在每个ConcreteBuilder中去实现
* @return 产品
*/
public abstract Product receiveResult();
}
public class ConcreteBuilder1 extends Builder {
private Product product = new Product();
@Override
public void buildPart1() {
// 构建产品第一部分
}
@Override
public void buildPart2() {
// 构建产品第二部分
}
@Override
public Product receiveResult() {
return product;
}
}
public class ConcreteBuilder2 extends Builder {
private Product product = new Product();
@Override
public void buildPart1() {
// 构建产品第一部分
}
@Override
public void buildPart2() {
// 构建产品第二部分
}
@Override
public Product receiveResult() {
return product;
}
}
/**
* Director
*/
public class Director {
/**
* 产品构造方法,负责调用各个零件建造方法
*/
public void construct(Builder builder) {
builder.buildPart1();
builder.buildPart2();
builder.receiveResult();
// 省略其他逻辑
}
}
public class Product {
// 产品属性省略
}
/**
* Client
*/
public class BuilderClient {
public static void main(String[] args) {
Director director = new Director();
Builder builder = null;
// 产品1
builder = new ConcreteBuilder1();
director.construct(builder);
// 产品2
builder = new ConcreteBuilder2();
director.construct(builder);
}
}
模式退化
- 省略Builder(抽象建造者),如果只需要一个ConcreteBuilder(具体建造者)的话,是可以省略掉Builder(抽象建造者)。
- 省略Director(导演者、监工),在ConcreteBuilder(具体建造者)只有一个的情况下,如果连Builder(抽象建造者)已经被省略掉,那么还可以进一步省略掉Director(导演者、监工)。
- 省略ConcreteBuilder(具体建造者),当建造者模式失去Builder(抽象建造者)和Director(导演者、监工)之后,还可以进一步退化、从而失去ConcreteBuilder(具体建造者)的情况。此时建造者角色与产品角色合并,从而使得产品自己就是自己的建造者。
当省略掉Builder、Director后,ConcreteBuilder角色自己扮演了导演和建造者的双重角色。AlertDialog.Builder、OkHttpClient.Builder、Retrofit.Builder就是省略了Builder、Director。
来看看OkHttpClient的源码(删减版):
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
static {
Internal.instance = new Internal() {
@Override public void addLenient(Headers.Builder builder, String line) {
builder.addLenient(line);
}
@Override public void addLenient(Headers.Builder builder, String name, String value) {
builder.addLenient(name, value);
}
@Override public RealConnectionPool realConnectionPool(ConnectionPool connectionPool) {
return connectionPool.delegate;
}
@Override public boolean equalsNonHost(Address a, Address b) {
return a.equalsNonHost(b);
}
@Override public int code(Response.Builder responseBuilder) {
return responseBuilder.code;
}
@Override
public void apply(ConnectionSpec tlsConfiguration, SSLSocket sslSocket, boolean isFallback) {
tlsConfiguration.apply(sslSocket, isFallback);
}
@Override public Call newWebSocketCall(OkHttpClient client, Request originalRequest) {
return RealCall.newRealCall(client, originalRequest, true);
}
@Override public void initExchange(
Response.Builder responseBuilder, Exchange exchange) {
responseBuilder.initExchange(exchange);
}
@Override public @Nullable Exchange exchange(Response response) {
return response.exchange;
}
};
}
final Dispatcher dispatcher;
final @Nullable Proxy proxy;
final List<Protocol> protocols;
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors;
final List<Interceptor> networkInterceptors;
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;
final CookieJar cookieJar;
final @Nullable Cache cache;
final @Nullable InternalCache internalCache;
final SocketFactory socketFactory;
final SSLSocketFactory sslSocketFactory;
final CertificateChainCleaner certificateChainCleaner;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int callTimeout;
final int connectTimeout;
final int readTimeout;
final int writeTimeout;
final int pingInterval;
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
// 省略
....
public Builder newBuilder() {
return new Builder(this);
}
public static final class Builder {
Dispatcher dispatcher;
@Nullable Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
@Nullable Cache cache;
@Nullable InternalCache internalCache;
SocketFactory socketFactory;
@Nullable SSLSocketFactory sslSocketFactory;
@Nullable CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int callTimeout;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
this.eventListenerFactory = okHttpClient.eventListenerFactory;
this.proxySelector = okHttpClient.proxySelector;
this.cookieJar = okHttpClient.cookieJar;
this.internalCache = okHttpClient.internalCache;
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
this.authenticator = okHttpClient.authenticator;
this.connectionPool = okHttpClient.connectionPool;
this.dns = okHttpClient.dns;
this.followSslRedirects = okHttpClient.followSslRedirects;
this.followRedirects = okHttpClient.followRedirects;
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
this.callTimeout = okHttpClient.callTimeout;
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
this.writeTimeout = okHttpClient.writeTimeout;
this.pingInterval = okHttpClient.pingInterval;
}
public Builder callTimeout(long timeout, TimeUnit unit) {
callTimeout = checkDuration("timeout", timeout, unit);
return this;
}
@IgnoreJRERequirement
public Builder callTimeout(Duration duration) {
callTimeout = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS);
return this;
}
public Builder connectTimeout(long timeout, TimeUnit unit) {
connectTimeout = checkDuration("timeout", timeout, unit);
return this;
}
@IgnoreJRERequirement
public Builder connectTimeout(Duration duration) {
connectTimeout = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS);
return this;
}
public Builder readTimeout(long timeout, TimeUnit unit) {
readTimeout = checkDuration("timeout", timeout, unit);
return this;
}
@IgnoreJRERequirement
public Builder readTimeout(Duration duration) {
readTimeout = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS);
return this;
}
public Builder writeTimeout(long timeout, TimeUnit unit) {
writeTimeout = checkDuration("timeout", timeout, unit);
return this;
}
@IgnoreJRERequirement
public Builder writeTimeout(Duration duration) {
writeTimeout = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS);
return this;
}
public Builder pingInterval(long interval, TimeUnit unit) {
pingInterval = checkDuration("interval", interval, unit);
return this;
}
@IgnoreJRERequirement
public Builder pingInterval(Duration duration) {
pingInterval = checkDuration("timeout", duration.toMillis(), TimeUnit.MILLISECONDS);
return this;
}
public Builder proxy(@Nullable Proxy proxy) {
this.proxy = proxy;
return this;
}
public Builder proxySelector(ProxySelector proxySelector) {
if (proxySelector == null) throw new NullPointerException("proxySelector == null");
this.proxySelector = proxySelector;
return this;
}
public Builder cookieJar(CookieJar cookieJar) {
if (cookieJar == null) throw new NullPointerException("cookieJar == null");
this.cookieJar = cookieJar;
return this;
}
public Builder cache(@Nullable Cache cache) {
this.cache = cache;
this.internalCache = null;
return this;
}
public Builder dns(Dns dns) {
if (dns == null) throw new NullPointerException("dns == null");
this.dns = dns;
return this;
}
public Builder socketFactory(SocketFactory socketFactory) {
if (socketFactory == null) throw new NullPointerException("socketFactory == null");
if (socketFactory instanceof SSLSocketFactory) {
throw new IllegalArgumentException("socketFactory instanceof SSLSocketFactory");
}
this.socketFactory = socketFactory;
return this;
}
public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
this.sslSocketFactory = sslSocketFactory;
this.certificateChainCleaner = Platform.get().buildCertificateChainCleaner(sslSocketFactory);
return this;
}
public Builder sslSocketFactory(
SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
if (trustManager == null) throw new NullPointerException("trustManager == null");
this.sslSocketFactory = sslSocketFactory;
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
return this;
}
public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) {
if (hostnameVerifier == null) throw new NullPointerException("hostnameVerifier == null");
this.hostnameVerifier = hostnameVerifier;
return this;
}
public Builder certificatePinner(CertificatePinner certificatePinner) {
if (certificatePinner == null) throw new NullPointerException("certificatePinner == null");
this.certificatePinner = certificatePinner;
return this;
}
public Builder authenticator(Authenticator authenticator) {
if (authenticator == null) throw new NullPointerException("authenticator == null");
this.authenticator = authenticator;
return this;
}
public Builder proxyAuthenticator(Authenticator proxyAuthenticator) {
if (proxyAuthenticator == null) throw new NullPointerException("proxyAuthenticator == null");
this.proxyAuthenticator = proxyAuthenticator;
return this;
}
public Builder connectionPool(ConnectionPool connectionPool) {
if (connectionPool == null) throw new NullPointerException("connectionPool == null");
this.connectionPool = connectionPool;
return this;
}
public Builder followSslRedirects(boolean followProtocolRedirects) {
this.followSslRedirects = followProtocolRedirects;
return this;
}
public Builder followRedirects(boolean followRedirects) {
this.followRedirects = followRedirects;
return this;
}
public Builder retryOnConnectionFailure(boolean retryOnConnectionFailure) {
this.retryOnConnectionFailure = retryOnConnectionFailure;
return this;
}
public Builder dispatcher(Dispatcher dispatcher) {
if (dispatcher == null) throw new IllegalArgumentException("dispatcher == null");
this.dispatcher = dispatcher;
return this;
}
public Builder protocols(List<Protocol> protocols) {
// Create a private copy of the list.
protocols = new ArrayList<>(protocols);
// Validate that the list has everything we require and nothing we forbid.
if (!protocols.contains(Protocol.H2_PRIOR_KNOWLEDGE)
&& !protocols.contains(Protocol.HTTP_1_1)) {
throw new IllegalArgumentException(
"protocols must contain h2_prior_knowledge or http/1.1: " + protocols);
}
if (protocols.contains(Protocol.H2_PRIOR_KNOWLEDGE) && protocols.size() > 1) {
throw new IllegalArgumentException(
"protocols containing h2_prior_knowledge cannot use other protocols: " + protocols);
}
if (protocols.contains(Protocol.HTTP_1_0)) {
throw new IllegalArgumentException("protocols must not contain http/1.0: " + protocols);
}
if (protocols.contains(null)) {
throw new IllegalArgumentException("protocols must not contain null");
}
// Remove protocols that we no longer support.
protocols.remove(Protocol.SPDY_3);
// Assign as an unmodifiable list. This is effectively immutable.
this.protocols = Collections.unmodifiableList(protocols);
return this;
}
public Builder connectionSpecs(List<ConnectionSpec> connectionSpecs) {
this.connectionSpecs = Util.immutableList(connectionSpecs);
return this;
}
public List<Interceptor> interceptors() {
return interceptors;
}
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}
public List<Interceptor> networkInterceptors() {
return networkInterceptors;
}
public Builder addNetworkInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
networkInterceptors.add(interceptor);
return this;
}
public Builder eventListener(EventListener eventListener) {
if (eventListener == null) throw new NullPointerException("eventListener == null");
this.eventListenerFactory = EventListener.factory(eventListener);
return this;
}
public Builder eventListenerFactory(EventListener.Factory eventListenerFactory) {
if (eventListenerFactory == null) {
throw new NullPointerException("eventListenerFactory == null");
}
this.eventListenerFactory = eventListenerFactory;
return this;
}
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
}
如何使用
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(20L, TimeUnit.SECONDS)
.readTimeout(20L, TimeUnit.SECONDS)
.writeTimeout(20L, TimeUnit.SECONDS)
.build();
可以看到最后的build方法既充当了建造模式中Builder的结果返还方法(receiveResult,返回对象OkHttpClient)又充当了Director中的构建方法(construct)来构建对象。
模式应用
(备注,摘自《Java与模式》)
- 需要生成的产品对象有复杂的内部结构。每一个内部成分本身可以是对象,也可以仅仅是一个对象(即产品对象)的一个组成部分。
- 需要生成的产品对象的属性互相依赖。建造者模式可以强制实行一种分步骤进行的建造过程,因此如果产品对象的一个属性必须在另一个属性被赋值之后才可以被赋值,使用建造模式便是一个很好的设计思想。
- 在对象创建过程中会使用系统中的其他一些对象,这些对象在产品对象的创建过程中不易得到。
模式效果
(备注,摘自《Java与模式》)
- 建造模式的使用使得产品内部表象可以独立地变化。使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 每一个Builder都相对独立,而与其他的Builder无关。
- 建造模式所建造的产品更易于控制。
附加
(备注,摘自《Java与模式》)