Builder模式:將一個複雜的對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
實用範圍
1、當創建複雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。
2、當構造過程必須允許被構造的對象有不同表示時。
Product是一個產品類,由四個部分組成,有的部分是必要的,有的是非必要的,用Builder模式的實現如下:
public class Product {
private final String part1; // 必傳參數
private final String part2; // 必傳參數
private final int part3; // 可選參數
private final String part4; // 可選參數
private User(UserBuilder builder) {
this.part1 = builder.part1;
this.part2 = builder.part2;
this.part3 = builder.part3;
this.part4 = builder.part4;
}
public String getPart1() {
return part1;
}
public String getPart2() {
return part2;
}
public int getPart3() {
return part3;
}
public String getPart4() {
return part4;
}
}
public class Builder {
private final String part1;
private final String part2;
private int part3;
private String part4;
4 public Builder(String part1, String part2) {
this.part1 = part2;
this.part1 = part2;
}
public Builder buildPart3(int part3) {
this.part3= part3;
return this;
}
public Builder buildPart4(String part4) {
this.part4= part4;
return this;
}
public User build() {
return new Product(this);
}
}
創建一個Product對象:
Product product = new Product().Builder(part1, part2)
.buildPart3(part3)
.buildPart4(part4)
.build();
其中:
Product類的構造方法是私有的。也就是說調用者不能直接創建Product對象。
Product類的屬性都是不可變的。所有的屬性都添加了final修飾符,並且在構造方法中設置了值。並且,對外只提供getters方法。
Builder模式使用了鏈式調用。可讀性更佳。
Builder的內部類構造方法中只接收必傳的參數,並且該必傳的參數適用了final修飾符。
下面一個例子是okHttp中request對象的構造:
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final RequestBody body;
final Object tag;
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<String> headers(String name) {
return headers.values(name);
}
public RequestBody body() {
return body;
}
public Object tag() {
return tag;
}
public Builder newBuilder() {
return new Builder(this);
}
public CacheControl cacheControl() {
CacheControl result = cacheControl;
return result != null ? result : (cacheControl = CacheControl.parse(headers));
}
public boolean isHttps() {
return url.isHttps();
}
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;
}
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);
}
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);
}
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder removeHeader(String name) {
headers.removeAll(name);
return this;
}
public Builder headers(Headers headers) {
this.headers = headers.newBuilder();
return this;
}
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(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, 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;
}
public Builder tag(Object tag) {
this.tag = tag;
return this;
}
/*
* Request 對象創建器,想得到一個Request 對象必須使用build 方法,
* 在方法中增加對Builder參數的驗證,並以異常的形式告訴給開發人員。
*/
public Request build() {
/**
* 比如下面判斷如果 url 是null的話就會拋出異常
*/
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
}
構造一個request對象:
Request request = new Request.Builder()
.url("http://www.weather.com.cn/data/sk/101010100.html")
.addHeader("header","header")
.put("RequestBody")
.build();