Builder设计模式

作者: OneXzgj | 来源:发表于2019-02-27 22:08 被阅读21次

    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的对象的过程。

    相关文章

      网友评论

        本文标题:Builder设计模式

        本文链接:https://www.haomeiwen.com/subject/uofksqtx.html