目录
一、现实编码中学习建造者模式
二、建造者模式的定义
三、简单例子,通俗易懂
3.1 普通的对象构建
3.1 建造者模式构建对象
四 总结
怎样使用Builder模式
在使用okhttp3的过程中,我们一般的使用过程如下:
public void testOkHttp() {
RequestBody requestBody = RequestBody.create(MediaType.parse("Content-Type, application/json"), "");
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url("https://test.api.com").post(requestBody).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, response.protocol() + " " + response.code() + " " + response.message());
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
Log.d(TAG, headers.name(i) + ":" + headers.value(i));
}
Log.d(TAG, "onResponse: " + response.body().string());
}
});
}
上面代码中我们可以看到构建Request的代码:
Request request = new Request.Builder().url("https://test.api.com").post(requestBody).build();
这里就使用到了建造者模式。
网络上随便找个定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
看起来十分的抽象,并不容易理解。
下面通过例子来理解,先将Request的源码修改一下,修改成普通的对象,如下:
import android.support.annotation.Nullable;
import java.util.List;
import okhttp3.CacheControl;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.RequestBody;
public final class Request {
private HttpUrl url;
private String method;
private Headers headers;
private RequestBody body;
private Object tag;
public Request(){
}
public Request(HttpUrl url,String method,Headers headers,RequestBody body, Object tag) {
this.url = url;
this.method = method;
this.headers = headers;
this.body = body;
this.tag = tag;
}
public void setUrl(HttpUrl url) {
this.url = url;
}
public HttpUrl url() {
return url;
}
public void setMethod(String method) {
this.method = method;
}
public String method() {
return method;
}
public void setHeaders(Headers headers) {
this.headers = headers;
}
public Headers headers() {
return headers;
}
public String header(String name) {
return headers.get(name);
}
public List headers(String name) {
return headers.values(name);
}
public void setBody(RequestBody body) {
this.body = body;
}
public @Nullable RequestBody body() {
return body;
}
public void setTag(Object tag) {
this.tag = tag;
}
public Object tag() {
return tag;
}
public boolean isHttps() {
return url.isHttps();
}
@Override public String toString() {
return "Request{method="
+ method
+ ", url="
+ url
+ ", tag="
+ (tag != this ? tag : null)
+ '}';
}
}
如果Request的代码如上,那么我们构建一个Request对象的步骤是直接通过构造函数构造一个Request对象,传入要求的参数,如果使用的默认构造函数,则还需要调用不同的属性的set方法设置初始值,说不定还会忘记,这里这是少量的参数,当参数类型多样,数量大的时候,构造对象实例那就更加的不方便和不易读了;
下面是Request的未改动源码:
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3;
import java.net.URL;
import java.util.List;
import javax.annotation.Nullable;
import okhttp3.internal.Util;
import okhttp3.internal.http.HttpMethod;
/**
* An HTTP request. Instances of this class are immutable if their {@link #body} is null or itself
* immutable.
*/
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Object tag;
private volatile CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
public HttpUrl url() {
return url;
}
public String method() {
return method;
}
public Headers headers() {
return headers;
}
public String header(String name) {
return headers.get(name);
}
public List headers(String name) {
return headers.values(name);
}
public @Nullable RequestBody body() {
return body;
}
public Object tag() {
return tag;
}
public Builder newBuilder() {
return new Builder(this);
}
/**
* Returns the cache control directives for this response. This is never null, even if this
* response contains no {@code Cache-Control} header.
*/
public CacheControl cacheControl() {
CacheControl result = cacheControl;
return result != null ? result : (cacheControl = CacheControl.parse(headers));
}
public boolean isHttps() {
return url.isHttps();
}
@Override public String toString() {
return "Request{method="
+ method
+ ", url="
+ url
+ ", tag="
+ (tag != this ? tag : null)
+ '}';
}
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
Object tag;
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.tag = request.tag;
this.headers = request.headers.newBuilder();
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
/**
* Sets the URL target of this request.
*
* @throws IllegalArgumentException if {@code url} is not a valid HTTP or HTTPS URL. Avoid this
* exception by calling {@link HttpUrl#parse}; it returns null for invalid URLs.
*/
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);
}
HttpUrl parsed = HttpUrl.parse(url);
if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
return url(parsed);
}
/**
* Sets the URL target of this request.
*
* @throws IllegalArgumentException if the scheme of {@code url} is not {@code http} or {@code
* https}.
*/
public Builder url(URL url) {
if (url == null) throw new NullPointerException("url == null");
HttpUrl parsed = HttpUrl.get(url);
if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
return url(parsed);
}
/**
* Sets the header named {@code name} to {@code value}. If this request already has any headers
* with that name, they are all replaced.
*/
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
/**
* Adds a header with {@code name} and {@code value}. Prefer this method for multiply-valued
* headers like "Cookie".
*
* Note that for some headers including {@code Content-Length} and {@code Content-Encoding},
* OkHttp may replace {@code value} with a header derived from the request body.
*/
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder removeHeader(String name) {
headers.removeAll(name);
return this;
}
/** Removes all headers on this builder and adds {@code headers}. */
public Builder headers(Headers headers) {
this.headers = headers.newBuilder();
return this;
}
/**
* Sets this request's {@code Cache-Control} header, replacing any cache control headers already
* present. If {@code cacheControl} doesn't define any directives, this clears this request's
* cache-control headers.
*/
public Builder cacheControl(CacheControl cacheControl) {
String value = cacheControl.toString();
if (value.isEmpty()) return removeHeader("Cache-Control");
return header("Cache-Control", value);
}
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 delete(@Nullable RequestBody body) {
return method("DELETE", body);
}
public Builder delete() {
return delete(Util.EMPTY_REQUEST);
}
public Builder put(RequestBody body) {
return method("PUT", body);
}
public Builder patch(RequestBody body) {
return method("PATCH", body);
}
public Builder method(String method, @Nullable RequestBody body) {
if (method == null) throw new NullPointerException("method == null");
if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
if (body != null && !HttpMethod.permitsRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must not have a request body.");
}
if (body == null && HttpMethod.requiresRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must have a request body.");
}
this.method = method;
this.body = body;
return this;
}
/**
* Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag
* is unspecified or null, the request is canceled by using the request itself as the tag.
*/
public Builder tag(Object tag) {
this.tag = tag;
return this;
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
}
相对应的构建Request对象的方式为:
Request request = new Request.Builder()
.url("https://test.api.com")
.post(requestBody)
.build();
采用链式调用,看起来构建过程十分清晰,一目了然。
其实在android中有很多地方使用了建造者模式,比如AlertDialog的创建
AlertDialog.Builder builder=new AlertDialog.Builder(this);
AlertDialog dialog=builder.setTitle("title")
.setIcon(android.R.drawable.ic_dialog_alert)
.setView(R.layout.myview)
.setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setNegativeButton(R.string.negative, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create();
dialog.show();
这里借鉴一篇写的简单易懂的文章:https://www.cnblogs.com/android-blogs/p/5530239.html
定义一个Person类代表人类的抽象,然后我们根据它来创建不同的人类,有不同的name,age,height和weight
public class Person {
private String name;
private int age;
private double height;
private double weight;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
public Person(String name, int age, double height, double weight) {
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
然后我们使用不同的构造函数创建实例人类:
Person personA=new Person();
Person personB=new Person("张三");
Person personC=new Person("李四",18);
Person personD=new Person("王五",21,170);
Person personE=new Person("赵六",17,181,173);
看上面的对象创建过程似乎也很简单,但是面对构造函数传入的数值的时候,并不能很容易的知道这些数字是什么意思。181是身高还是体重,173是体重还是身高。
将上面例子修改一下如下:
public class Person {
private String name;
private int age;
private double height;
private double weight;
privatePerson(Builder builder) {
this.name=builder.name;
this.age=builder.age;
this.height=builder.height;
this.weight=builder.weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
static class Builder{
private String name;
private int age;
private double height;
private double weight;
public Builder name(String name){
this.name=name;
return this;
}
public Builder age(int age){
this.age=age;
return this;
}
public Builder height(double height){
this.height=height;
return this;
}
public Builder weight(double weight){
this.weight=weight;
return this;
}
public Person build(){
return new Person(this);
}
}
}
使用如下,一下就清晰了,也能清楚的知道赋值的是什么属性
Person.Builder builder=new Person.Builder();
Person person=builder
.name("赵六")
.age(17)
.height(181)
.weight(173)
.build();
这里总结一下通用步骤:
1)在普通的类中定义一个静态内部类Builder,Builder内部的成员变量和外部类成员变量一一对应,也就是一样的;
2)Builder类中定义一系列的方法,用于给Builder中的成员变量赋值,并返回当前对象本身,也就是返回this关键字,这里方便链式调用;
3)Builder类提供一个build方法用于创建外部类的实例对象,该方法内部调用了外部类的一个私有构造函数,该构造函数的参数就是内部类Builder;
4)外部类提供一个私有构造函数供内部类调用,在该构造函数中完成成员变量的赋值,取值为Builder对象中对应的值。