模式定義
建造者模式:將一個復雜產品的創建與表示分離,使得同樣的創建過程可以創建不同的表示
客戶端不用去關心產品對象的內部組成,只需要關注如何一步一步的去創建復雜對象,建造者模式是一種創建型模式,何為創建型模式? 顧名思義就是創建對象的設計模式,如常用的單例模式,工廠模式等都為創建型模式。
模式結構
建造者模式主要有以下四個角色:
- Builder 抽象建造者
- ConcreteBuilder 具體建造者
- Director 指揮者
- Product 具體產品
代碼分析
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對象并完成了對象的創建。
總結
- 建造者模式屬于創建型模式,將一個復雜產品的創建與表示相分離,同樣的構建過程有不同的表示。何為復雜產品?其實就是成員屬性比較多的產品,客戶端無需關心復雜產品的內部構造組成細節,只需要知道具體的建造者類型即可,更多關注如何一步一步創建一個復雜對象。
- 建造者模式優點:客戶端無需知道產品內部內部構造組成細節,將產品本身與產品的創建過程解耦;具體的建造者時間相互獨立,便于替換或增加新的建造者,客戶端通過不同的建造者可以得到不同對象。
- 建造者模式缺點:建造者創建的產品一般具有具有較多相同共同點,其內部組成類似,如果產品之間差異性較大,則不適合用建造者模式,其次,如果產品內部變化復雜,會導致創建很多具體的建造者來對應其變化,這樣會導致系統變的很龐大。
- 建造者模式使用場景:需要創建的產品有復雜的內部結構,也就是說成員屬性較多,且產品成員屬性之間相互依賴,需指定生成順序。