java的設計模式 - Builder模式

Builder 模式的目的?

構造對象的方式過於複雜,不如將之抽離出來。好比,構造器參數過多
這樣說也有點抽象,舉個例子吧。html

舉個例子

好比 很是熱門的消息隊列RabbitMQAMQP.BasicPropertiesjava

由於它的屬性比較多,因此構造函數也是挺嚇人的。程序員

我看到也不太想調用。
若是如今要構造一條消息安全

  • 投遞模式(delivery mode)爲 2
  • 優先級(priority)是 2
  • content-type 爲 text/plain

在沒有 builder 模式以前,是這樣構造的多線程

new AMQP.BasicProperties("text/plain",null,null,2,1,null,null,null,null,null,null,null,null,null);

痛苦啊!!!不信,你本身也能夠嘗試構造一下。ide

  • 構造函數有不少你不想設置的參數
  • 你要看準,哪一個參數要賦值,哪一個參數不賦值,一不當心就可能出錯了。而這裏有 14 個參數。。。
  • 維護性差,寫完代碼再看一下,也看不出這個參數到底是什麼意思。還要點進去,一個一個參數地看才知道是什麼意思

而用了 Builder 模式後。函數

new AMQP.BasicProperties.Builder()
    .contentType("text/plain")
    .deliveryMode(2)
    .priority(1)
    .build();

舒暢!!!ui

Builder 是如何實現?

很簡單。this

  • BasicProperties中添加一個叫Builder的內部類
  • Builder 中全部字段和BasicProperties類是徹底一致的
  • Builder實例在調用build函數的時候,再調用BasicProperties的構造函數構造對象。

代碼以下線程

public static class BasicProperties{
    private String contentType;
    private String contentEncoding;
    private Map<String,Object> headers;
    private Integer deliveryMode;
    private Integer priority;
    //... 還有不少屬性

    public BasicProperties(
        String contentType,
        String contentEncoding,
        Map<String,Object> headers,
        Integer deliveryMode,
        //...
        String clusterId){
            this.contentType = contentTypel;
            this.contentEncoding = contentEncoding;
            //...
    }

    public static final class Builder {
        private String contentEncoding;
        private Map<String,Object> headers;
        private Integer deliveryMode;
        private Integer priority;
        //.. 和BasicProperties的字段一致的。
        
        public Builder contentType(String contentType){
            this.contentType = contentType; 
            return this; 
        }
    
        public Builder contentEncoding(String contentEncoding){
            this.contentEncoding = contentEncoding; 
            return this; 
        }
        
        public BasicProperties build() {
            return new BasicProperties
                ( contentType
                  contentEncoding,
                  //還有不少屬性
                );
        }
    }
}

分析

Builder 模式的好處

  • 不用花太多心思去記構造器的順序,在 ide 中輸入一個點就有自動提示了
  • 好維護,很容易看到看明白這是什麼屬性

壞處

  • 構造對象就要先調用 Buidler 構造器,多了構造器的開銷
  • 類的關係變得複雜了

其餘的作法

若是不用 Builder 模式,有其餘的作法嗎?

重疊構造器?

好比,上面的例子,我構造的消息只需 投遞模式(delivery mode)、優先級(priority)、 content-type ,專門爲這幾個參數弄個專門的構造函數,能夠嗎?
調用就變成這樣了。

new AMQP.BasicProperties("text/plain",2,1)

能夠,

  • 但依然不太好看。
  • 若是有不一樣的需求,各類屬性都排列組合一下也麻煩。
  • 不實際,由於類字段的類型可能會是同樣的,有些組合註定不行

javaBean 模式呢?

BasicProperties  p = new AMQP.BasicProperties();
p.setContentType("text/plain");
p.setDeliveryMode(2);
p.setPriority(1);

在《effective java》中就探討過這個可能,書中是這樣說的

由於構造過程被分到幾個調用中,在構造過程當中 javaBean 可能處於不一致的狀態。類沒法僅僅經過檢驗構造器參數的有效性來保證一致性。試圖使用處於不一致狀態的對象,將致使失敗,這種失敗與包含錯誤的代碼截然不同,所以它調試起來十分困難。與此相關的另外一點不足在於,javaBeans 模式阻止了把類作成不可變的可能,這須要程序員付出格外的努力來確保它的線程安全。

這話就有點摸不着頭腦,什麼意思

其實意思是大概的,

  1. javaBean 是構造與字段賦值分離的,有可能 線程 1 在給對象 Obj 賦值,尚未賦完成的時候,線程 2 就拿了 Obj 的值了,就不一致了
  2. 若是 Obj 的字段全都是 final 的,不會出現上面那種狀況,但字段只能會經過構造函數賦值(builder 模式也行),不能使用 javaBeans 的 setXXX 函數賦值了。
  3. 對多線程要求的,好比是傳給消息隊列的對象,程序員要保證下線程安全。
  4. 這也是個一個開放開閉的問題,Javabean 這樣的寫法確實和徹底開放沒啥區別,若是字段肯定下來不用改了就最好設爲 final 。

以上

相關文章
相關標籤/搜索