为了账号安全,请及时绑定邮箱和手机立即绑定

Builder设计模式

标签:
Android

1.定义以及UML建模图:

将一个复杂的对象的构建与它的表示分离,是的同样的构建过程可以创建不同的表示.


https://img1.sycdn.imooc.com//5d328a66000143ad07080309.jpg

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


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消