Java設計模式-構建者Builder模式

bohp2357 6年前發布 | 40K 次閱讀 設計模式 Java Java開發

構建者Builder模式是Java中十分常見的一種設計模式,先看一下GOF設計模式一書中給它的定義:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。

構造者Builder模式類圖如下:

一般而言,Builder模式有四個組成部分:

Builder為創建一個Product對象的各個部件指定抽象接口。

ConcreteBuilder實現Builder的接口以構造和裝配該產品的各個部件、定義并明確它所創建的表示、提供一個檢索產品的接口。

Director構造一個使用Builder接口的對象。

Product表示被構造的復雜對象。ConcreteBuilder創建該產品的內部表示并定義它的裝配過程。包含定義組成部件的類,包括將這些部件裝配成最終產品的接口。

Builder模式示例

下面通過一個簡單的示例介紹Builder模式。

首先創建一個Product產品類。

public class Product {
 
 private String header;
 private String body;
 private String footer;
 
 public void setHeader(String header) {
 this.header = header;
 }
 
 public void setBody(String body) {
 this.body = body;
 }
 
 public void setFooter(String footer) {
 this.footer = footer;
 }
 
 public String toString() {
 return "Product [header=" + header + ", body=" + body + ", footer="
 + footer + "]";
 }
}

其次創建Builder接口。

public interface Builder {
 
 void buildHeader();
 
 void buildBody();
 
 void buildFooter();
 
 Product build();
 
}

然后創建一個ConcreteBuilder 類,即實現Builder接口。

public class ConcreteBuilderA implements Builder {
 
 private Product product;
 
 public ConcreteBuilderA() {
 product = new Product();
 }
 
 public void buildHeader() {
 product.setHeader("Header_A");
 }
 
 public void buildBody() {
 product.setBody("Body_A");
 }
 
 public void buildFooter() {
 product.setFooter("Footer_A");
 }
 
 public Product build() {
 return product;
 }
 
}

最后創建一個Director類。

public class Director {
 
 private Builder builder;
 
 public Director(Builder builder) {
 this.builder = builder;
 }
 
 public void buildProduct() {
 this.builder.buildHeader();
 this.builder.buildBody();
 this.builder.buildFooter();
 }
 
 public Product build() {
 return this.builder.getProduct();
 }
 
}

接下來測試一下Builder模式示例。

Builder builderA = new ConcreteBuilderA();
 
Director director = new Director(builderA);
director.buildProduct();
Product product = director.build();
System.out.println(product.toString());
//Product [header=Header_A, body=Body_A, footer=Footer_A]

這樣我們也可以通過繼承Builder接口,接下來創建ProductB、ProductC等等,甚至有一天可以創建一個ProductX,上述代碼調用順序以及邏輯幾乎不需要做大的變動,只需要再擴展一個ConcreteBuilder即可。

Builder模式特點

它使你可以改變一個產品的內部表示。Builder對象提供給Director一個構造產品的抽象接口。該接口使得生成器可以隱藏這個產品的表示和內部結構。它同時也隱藏了該產品是如何裝配的。因為產品是通過抽象接口構造的,你在改變該產品的內部表示時所要做的只是定義一個新的生成器。

它將構造代碼和表示代碼分開。Builder模式通過封裝一個復雜對象的創建和表示方式提高了對象的模塊性。客戶不需要知道定義產品內部結構的類的所有信息;這些類是不出現在Builder接口中的。每個ConcreteBuilder包含了創建和裝配一個特定產品的所有代碼。這些代碼只需要寫一次;然后不同的Director可以復用它以在相同部件集合的基礎上構作不同的Product。

它可對構造過程進行更精細的控制。Builder模式與一下子就生成產品的創建型模式不同,它是在Director的控制下一步一步構造產品的。僅當該產品完成時Director才從生成器中取回它。因此Builder接口相比其他創建型模式能更好的反映產品的構造過程。這使可以更精細的控制構建過程,從而能更精細的控制所得產品的內部結構。

通常有一個抽象的Builder類為Director可能要求創建的每一個構件定義一個操作。這些操作缺省情況下什么都不做。一個ConcreteBuilder類對它有興趣創建的構件重定義這些操作。

Builder模式的變體

如果ConcreteBuilder只需要一個,只是為了個性化設置Product中的每個屬性,那么Director也同樣失去了它的意義了。既然ConcreteBuilder只有一個實現者,這時候我們的Builder設置為接口就有些冗余了,我們可將Builder設置為Product的一個內部類。為了更安全的考慮,Product中每個屬性外界都必須通過Builder設置才可以,這種情況就需要將Product屬性設置為final屬性,然后通過私有構造方法設置。z最后一切從簡,將創建Product的方法buildProduct()也剔除了,讓Builder中的屬性設置方法都返回一個Builder類型,直接return它自己即可,在創建方法build中通過鏈式調用直接返回一個Product對象。

下面是示例源代碼:

public class Product {
 
 private final String header;
 private final String body;
 private final String footer;
 
 private Product(Builder builder) {
 this.header = builder.header;
 this.body = builder.body;
 this.footer = builder.footer;
 }
 
 public static class Builder {
 private String header;
 private String body;
 private String footer;
 
 public Builder setHeader(String header) {
 this.header = header;
 return this;
 }
 
 public Builder setBody(String body) {
 this.body = body;
 return this;
 }
 
 public Builder setFooter(String footer) {
 this.footer = footer;
 return this;
 }
 
 public Product build() {
 return new Product(this);
 }
 
 }
 
 public String toString() {
 return "Product [header=" + header + ", body=" + body + ", footer="
 + footer + "]";
 }
}

在使用的時候也相當方面,如下所示:

Product product = new Product.Builder()
 .setHeader("Header")
 .setBody("Body")
 .setFooter("Footer")
 .build();
 
System.out.println(product.toString());
//Product [header=Header, body=Body, footer=Footer]

這種寫法在Android開發中很常見,例如AlertDialog的創建方式如下:

AlertDialog.Builder builder=new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon);
builder.setTitle("Title");
builder.setMessage("Message");
...
builder.create().show();

還有一些比較常用的框架都是采用的這種變體Builder模式,如okhttp中創建Request,以及retrofit框架中創建一個Retrofit對象。

Request request = new Request.Builder()
        .url("url")
        .build();
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("url")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

還有比較常用的加載圖片的框架如Universal-Image-Loader,這里代碼就不再貼出來了。

這種Builder模式,也有一個缺點就是一旦調用build()方法構造一個實體之后,就不能再更改其它屬性了,因為build()方法返回的是Product,這本身是一種保護機制,也是Builder模式的特性。但是在實際開發中必須考慮一種情況,就是即使已經返回了Product還是需要重新設置一個新的屬性值,有些開發者可能說了,再重新Builder一次即可,這種方式也未嘗不可以。可是考慮到代碼的復用性上面,前面一個鏈式調用幾十個屬性值返回了一個Product,而現在需要更改的僅僅是一個或者極少幾個屬性就可以了獲取所需要的新Product了,作為軟件開發人員這時候不免不會產生疑問,還有其它更好地方式嗎?那么,答案是肯定的,從使用的框架中也可以一探究竟。

在OkHttp中就大量使用了這種方式,如Request類中:

public Builder newBuilder() {
 return new Builder(this);
}
Builder(Request request) {
  this.url = request.url;
  this.method = request.method;
  this.body = request.body;
  this.tag = request.tag;
  this.headers = request.headers.newBuilder();
}

通過這種方式我們又可以拿到Buidler對象了,而且以前設置的屬性也都被重新設置進了當前返回的Builder對象中了,這樣就又可以重新設置部分屬性了。

實際上在Android Studio中已經有人提供了插件模板進行Builder創建了。在插件中搜索builder,然后選中InnerBuilder插件,下載安裝即可。

Builder設計模式因其簡潔且封裝性高在Android開發當中使用頻率非常的高。Factory工廠模式與Builder模式相似,因為它也可以創建復雜對象。主要的區別是Builder模式著重于一步步構造一個復雜對象。而Factory著重于多個系列的產品對象(簡單的或是復雜的)。Builder在最后的一步返回產品,而對于Factory工廠來說,產品是立即返回的。

 

來自:http://www.sunnyang.com/754.html

 

 本文由用戶 bohp2357 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!