OkHttp源码中的建造者模式之所以有必要单独拿出来讲,是因为OkHttp 3.x和4.x分别用Java语言和Kotlin语言写的,所以需要做一个对比分析。
在OkHttp的源码中搜索“Builder”,可以看到OkHttp的OkHttpClient、Request和Response等很多类的代码里包含名为Builder的子类,这些都是建造者模式的应用。
OkHttpClient.Builder
OkHttpClient是一个内部及其复杂的类,内部包含一系列超时时间(Timeout),代理(proxy),缓存(cache),分发器(dispatcher),拦截器(interceptors)等等。OkHttpClient.Builder是建造者模式的典型应用。
以下是3.x的实现:
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
// ……代码省略……
public Builder newBuilder() {
return new Builder(this);
}
public static final class Builder {
// ……代码省略……
public Builder() {
dispatcher = new Dispatcher();
// ……代码省略……
pingInterval = 0;
}
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
// ……代码省略……
this.pingInterval = okHttpClient.pingInterval;
}
//
public Builder callTimeout(long timeout, TimeUnit unit) {
callTimeout = checkDuration("timeout", timeout, unit);
return this;
}
// ……代码省略……
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}
public Builder addNetworkInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
networkInterceptors.add(interceptor);
return this;
}
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
}
以下是4.x的实现
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
constructor() : this(Builder())
class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher()
// ……代码省略……
internal var routeDatabase: RouteDatabase? = null
internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher
// ……代码省略……
this.routeDatabase = okHttpClient.routeDatabase
}
fun addInterceptor(interceptor: Interceptor) = apply {
interceptors += interceptor
}
fun addNetworkInterceptor(interceptor: Interceptor) = apply {
networkInterceptors += interceptor
}
// ……代码省略……
fun cookieJar(cookieJar: CookieJar) = apply {
this.cookieJar = cookieJar
}
@IgnoreJRERequirement
fun readTimeout(duration: Duration) = apply {
readTimeout(duration.toMillis(), MILLISECONDS)
}
@IgnoreJRERequirement
fun writeTimeout(duration: Duration) = apply {
writeTimeout(duration.toMillis(), MILLISECONDS)
}
fun build(): OkHttpClient = OkHttpClient(this)
}
}
Request.Builder
Request是发送出去的请求,与Response相对。
以下是3.x的实现:
public final class Request {
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
// ……代码省略……
public Builder newBuilder() {
return new Builder(this);
}
public static class Builder {
@Nullable HttpUrl url;
String method;
Headers.Builder headers;
@Nullable RequestBody body;
Map<Class<?>, Object> tags = Collections.emptyMap();
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tags = request.tags.isEmpty()
? Collections.emptyMap()
: new LinkedHashMap<>(request.tags);
this.headers = request.headers.newBuilder();
}
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
// ……代码省略……
public Builder get() {
return method("GET", null);
}
public Builder head() {
return method("HEAD", null);
}
public Builder post(RequestBody body) {
return method("POST", body);
}
public Builder tag(@Nullable Object tag) {
return tag(Object.class, tag);
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
}
以下是4.x的实现:
class Request internal constructor(
@get:JvmName("url") val url: HttpUrl,
@get:JvmName("method") val method: String,
@get:JvmName("headers") val headers: Headers,
@get:JvmName("body") val body: RequestBody?,
internal val tags: Map<Class<*>, Any>
) {
// ……代码省略……
fun newBuilder(): Builder = Builder(this)
// ……代码省略……
open class Builder {
internal var url: HttpUrl? = null
internal var method: String
internal var headers: Headers.Builder
internal var body: RequestBody? = null
/** A mutable map of tags, or an immutable empty map if we don't have any. */
internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()
constructor() {
this.method = "GET"
this.headers = Headers.Builder()
}
internal constructor(request: Request) {
this.url = request.url
this.method = request.method
this.body = request.body
this.tags = if (request.tags.isEmpty()) {
mutableMapOf()
} else {
request.tags.toMutableMap()
}
this.headers = request.headers.newBuilder()
}
open fun url(url: HttpUrl): Builder = apply {
this.url = url
}
open fun url(url: String): Builder {
// Silently replace web socket URLs with HTTP URLs.
val finalUrl: String = when {
url.startsWith("ws:", ignoreCase = true) -> {
"http:${url.substring(3)}"
}
url.startsWith("wss:", ignoreCase = true) -> {
"https:${url.substring(4)}"
}
else -> url
}
return url(finalUrl.toHttpUrl())
}
open fun url(url: URL) = url(url.toString().toHttpUrl())
// ……代码省略……
open fun get() = method("GET", null)
open fun head() = method("HEAD", null)
open fun post(body: RequestBody) = method("POST", body)
open fun method(method: String, body: RequestBody?): Builder = apply {
require(method.isNotEmpty()) {
"method.isEmpty() == true"
}
if (body == null) {
require(!HttpMethod.requiresRequestBody(method)) {
"method $method must have a request body."
}
} else {
require(HttpMethod.permitsRequestBody(method)) {
"method $method must not have a request body."
}
}
this.method = method
this.body = body
}
// ……代码省略……
open fun build(): Request {
return Request(
checkNotNull(url) { "url == null" },
method,
headers.build(),
body,
tags.toImmutableMap()
)
}
}
}
用到了apply()函数
建造者模式在OkHttp源码中的应用极其广泛,除了以上提到的之外,还有Cookie.Builder、Response.Builder、FormBody.Builder等用到了建造者模式,本文不做过多讨论。