1.定义以及UML建模图:
将一个复杂的对象的构建与它的表示分离,是的同样的构建过程可以创建不同的表示.
Builder设计模式.png
2.使用场景:
多个部件或者零件,都可以装配到一个对象中,但是产生的运行结果又相同
产品类非常复杂,或者产品类中调用顺序不同产生了不同的作用
初始化一个对象特别复杂,如使用多个构造方法,或者说有很多参数,并且都有默认值时。
3.核心类
(1) 抽象产品类 computer
(2) Builder 抽象Builder,规范产品的组建,一般是由子类实现具体的组建过程。
(3)MacbookBuilder 具体的Builder类,具体的创建对象的类。
(4) Directror 统一组建过程
以组装一台计算机为主,主要进行组装主机,设置操作系统,设置显示器。
4.具体使用:
4.1 计算机抽象类,Computer
public abstract class Computer { protected String mBoard; protected String mDisplay; protected String mOs; protected Computer(){ } public void setBoard(String board){ mBoard=board; } public void setDisplay(String display) { this.mDisplay = display; } public abstract void setOs() ; @Override public String toString() { return "Computer{" + "mBoard='" + mBoard + '\'' + ", mDisplay='" + mDisplay + '\'' + ", mOs='" + mOs + '\'' + '}'; } }
4.2 具体的MacBook ,具体产品类
public class MacBook extends Computer{ protected MacBook() { } @Override public void setOs() { mOs="Mac OS X 12"; } }
4.3 抽象Builder
public abstract class Builder { abstract void buildBoard(String board); abstract void buildDisplay(String display); abstract void buildOs(); abstract Computer build(); }
4.4 具体的Builder
public class MacBookBuilder extends Builder { private Computer mComputer=new MacBook(); @Override void buildBoard(String board) { mComputer.setBoard(board); } @Override void buildDisplay(String display) { mComputer.setDisplay(display); } @Override void buildOs() { mComputer.setOs(); } @Override Computer build() { return mComputer; } }
4.5 Director类,负责具体的构造Computer
public class Director { Builder mBuilser=null; public Director(Builder builer) { this.mBuilser = builer; } public void construct(String board,String display){ mBuilser.buildDisplay(display); mBuilser.buildBoard(board); mBuilser.buildOs(); } }
4.6 具体使用
public class Test { public static void main(String[] args){ Builder builder=new MacBookBuilder(); Director pcDirector=new Director(builder); pcDirector.construct("英特尔主板","Retina显示器"); Computer computer = builder.build(); System.out.println(computer.toString()); } }
5.总结
为了灵活构造复杂对象,该对象会有多个成员变量,在外部调用的时候,不需要或者不方便一次性创建出所有的成员变量,在这种情况下,使用多个构造方法去构建对象,很难维护,这时候Builder设计模式解决这个问题,进行buid()方法中创建对象,并且将builder传入,该builder中,维护了传入对象的成员变量。
6. Okhttp中的Request对象使用的Builder设计模式分析
如果你研究过Okhttp中的源码,对Request对象应该很熟悉,接下来看看Request是如何实现链式一句话创建的。
以下是okhttp中的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 @Nullable String header(String name) { return headers.get(name); } public List<String> 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". * * <p>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; } /** Removes all headers named {@code name} on this builder. */ 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("http://www.baidu.com") .get() .addHeader("Cache-Control", "public, max-age=60") .build();
通过一句话,将请求方法,请求地址,设置请求头信息,都已经做好了。
简单分析一下其原理
具体的产品类,即需要构造的类 Request
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 @Nullable String header(String name) { return headers.get(name); } public List<String> 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) + '}'; }
具体Builder类:
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". * * <p>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; } /** 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); } /** * 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); } }
可以看到Okhttp中Request的使用到的Builder设计模式更灵活,没有进行抽象类约束,因为在Okhttp中,只生成request这一个对象,所以直接使用具体Builder和具体的产品直接进行构建组建自己。而且在构建完部件以后,直接返回该Builder对象,方便链式调用,也达到了一句话创建复杂request的对象的过程。
作者:OneXzgj
链接:https://www.jianshu.com/p/f958ba891467
共同学习,写下你的评论
评论加载中...
作者其他优质文章