設計模式之建造者模式

模式定義

建造者模式:將一個復雜產品的創建與表示分離,使得同樣的創建過程可以創建不同的表示
客戶端不用去關心產品對象的內部組成,只需要關注如何一步一步的去創建復雜對象,建造者模式是一種創建型模式,何為創建型模式? 顧名思義就是創建對象的設計模式,如常用的單例模式,工廠模式等都為創建型模式。

模式結構

建造者模式主要有以下四個角色:

  • Builder 抽象建造者
  • ConcreteBuilder 具體建造者
  • Director 指揮者
  • Product 具體產品
Builder.jpg

代碼分析

Product:具體創建的產品對象

public class Product {

    private String partA;
    private String partB;
    private String partC;
    
    public String getPartA() {
        return partA;
    }
    public void setPartA(String partA) {
        this.partA = partA;
    }
    public String getPartB() {
        return partB;
    }
    public void setPartB(String partB) {
        this.partB = partB;
    }
    public String getPartC() {
        return partC;
    }
    public void setPartC(String partC) {
        this.partC = partC;
    }
    
    
}

Builder:抽象建造者

public abstract class Builder {
    public Product product = new Product();
    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract void buildPartC();
    public Product getResult() {
        return product;
    }

}

ConcreteBuilder:具體建造者


public class ConcreteBuilder extends Builder{
    

    @Override
    public void buildPartA() {
        // TODO Auto-generated method stub
        product.setPartA("partA");
        
    }

    @Override
    public void buildPartB() {
        // TODO Auto-generated method stub
        product.setPartB("partB");
        
    }

    @Override
    public void buildPartC() {
        // TODO Auto-generated method stub
        product.setPartC("partC");
        
    }

}

Director:指揮者

public class Director {

    private Builder builder;
    public Director(Builder builder) {
        this.builder = builder;
        
    }
    public Product construct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getResult();
    }

}

測試及結果:

public class Test {
    public static void main(String[] args) {
        
        ConcreteBuilder builder = new ConcreteBuilder();
        Director director = new Director(builder);
        Product product = director.construct();
        System.out.println(product.getPartA());
        System.out.println(product.getPartB());
        System.out.println(product.getPartC());
        
    }
    

}

從測試代碼來看,客戶端只需要知道具體的創建者類型,并通過指揮者就可以創建想要的對象,無需關系產品內部的具體構造細節,Builder抽象定義了產品的創建方法和返回方法,而真正的實現是ConcreteBuilder,我們也可以定義不同的ConcreteBuilder,可以定義多個ConcreteBuilder。對于客戶端而言,Director分離了產品的創建過程,通過調用建造者的一系列方法返回一個完整的產品對象,這樣就做到了所謂的創建與表示相分離,且同樣的創建過程有不同的表示。在我們日常的使用中,如果只有一個具體的建造者,通常會省略掉具體的建造者和指揮者,Builder自身充當指揮者角色,下面我們通過分析時下比較流行的網絡框架Retrofit的創建過程,它就是采用Builder模式來創建對象。PS:很多不錯的開源框架都用到了Builder設計模式

實例分析

接下來首先看一下retrofit對象創建的代碼

 Retrofit retrofit = new Retrofit.Builder()
                            .baseUrl("http:www.baidu.com")
                            .addConverterFactory(ScalarsConverterFactory.create())
                            .addConverterFactory(GsonConverterFactory.create())
                            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                            .build();

retrofit對象創建采用了建造者模式,只不過它省略了ConcreteBuilder和Director這倆個角色,我們來看一下它的源碼,這里并不是Retrofit這個類的所有源代碼,我刪除了一些跟本文內容無關的代碼


public final class Retrofit {
  

  final okhttp3.Call.Factory callFactory;
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> adapterFactories;
  final Executor callbackExecutor;
  final boolean validateEagerly;

  Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
      Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory;
    this.baseUrl = baseUrl;
    this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
    this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
  }


  public okhttp3.Call.Factory callFactory() {
    return callFactory;
  }

  /** The API base URL. */
  public HttpUrl baseUrl() {
    return baseUrl;
  }

  public List<CallAdapter.Factory> callAdapterFactories() {
    return adapterFactories;
  }


  public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }


  public List<Converter.Factory> converterFactories() {
    return converterFactories;
  }
  
  public Executor callbackExecutor() {
    return callbackExecutor;
  }

  public Builder newBuilder() {
    return new Builder(this);
  }

  
  public static final class Builder {
    private final Platform platform;
    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
      converterFactories.add(new BuiltInConverters());
    }

    public Builder() {
      this(Platform.get());
    }

    Builder(Retrofit retrofit) {
      platform = Platform.get();
      callFactory = retrofit.callFactory;
      baseUrl = retrofit.baseUrl;
      converterFactories.addAll(retrofit.converterFactories);
      adapterFactories.addAll(retrofit.adapterFactories);
      // Remove the default, platform-aware call adapter added by build().
      adapterFactories.remove(adapterFactories.size() - 1);
      callbackExecutor = retrofit.callbackExecutor;
      validateEagerly = retrofit.validateEagerly;
    }

   
    public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
    }

    public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = checkNotNull(factory, "factory == null");
      return this;
    }

   
    public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }

   
    public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      List<String> pathSegments = baseUrl.pathSegments();
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      return this;
    }

   
    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

   
    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

   
    public Builder callbackExecutor(Executor executor) {
      this.callbackExecutor = checkNotNull(executor, "executor == null");
      return this;
    }

  
    public Builder validateEagerly(boolean validateEagerly) {
      this.validateEagerly = validateEagerly;
      return this;
    }

    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
  }
}

首先我們來分析下源碼中的角色,其中有倆個角色,分別為Retrofit類和其靜態內部類Builder,結合之前的建造者模式角色分析,這里的Retrofit其實就是我們要創建的產品Product,但是這里的Builder并不是一個抽象類,而且沒有發現之前所分析的具體建造者ConcreteBuilder和指揮者Director,正如上面所說,當只有一個具體的建造者時,我們可以省略掉具體建造者這個角色,如果省略掉具體建造者,那么指揮者也可以一并省略,Builder自身充當建造者與指揮者雙角色。那Builder是如何充當這倆個角色的呢?在Builder類中有一系列公開方法且返回值都是自身,其實這些方法就是對Retrofit的成員屬性進行了實例化和賦值,Builder類中還有一個重要的Build方法,該方法的返回值是我們要創建的對象Retrofit,Build方法中首先對Retrofit的相關成員屬性做了判空操作,最后調用了Retrofit的構造方法返回Retrofit對象并完成了對象的創建。

總結

  • 建造者模式屬于創建型模式,將一個復雜產品的創建與表示相分離,同樣的構建過程有不同的表示。何為復雜產品?其實就是成員屬性比較多的產品,客戶端無需關心復雜產品的內部構造組成細節,只需要知道具體的建造者類型即可,更多關注如何一步一步創建一個復雜對象。
  • 建造者模式優點:客戶端無需知道產品內部內部構造組成細節,將產品本身與產品的創建過程解耦;具體的建造者時間相互獨立,便于替換或增加新的建造者,客戶端通過不同的建造者可以得到不同對象。
  • 建造者模式缺點:建造者創建的產品一般具有具有較多相同共同點,其內部組成類似,如果產品之間差異性較大,則不適合用建造者模式,其次,如果產品內部變化復雜,會導致創建很多具體的建造者來對應其變化,這樣會導致系統變的很龐大。
  • 建造者模式使用場景:需要創建的產品有復雜的內部結構,也就是說成員屬性較多,且產品成員屬性之間相互依賴,需指定生成順序。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容