2016-04-05 33 views
3

因此,在着名的Effective Java書中,它引入了一個構建模式,您可以在其中有一個內部靜態生成器類來實例化一個類。這本書提出的一類以下設計:正如Effective Java中所描述的,嵌套的Builder類是非常必要的嗎?

public class Example { 
    private int a; 
    private int b; 

    public static class Builder() { 
     private int a; 
     private int b; 

     public Builder a(int a) { 
      this.a = a; 
      return this; 
     } 

     public Builder b(int b) { 
      this.b = b; 
      return this; 
     } 

     public Example build() { 
      return new Example(this);  
     } 
    } 

    private Example(Builder builder) { 
     this.a = builder.a; 
     this.b = builder.b; 
    } 
} 

但是我沒能理解爲什麼我們真的需要一個內部Builder class上面的代碼有重複的字段聲明行(int a,b),如果我們有更多的字段,這將變得相對混亂。

爲什麼不只是擺脫Builder類,並讓Example類承擔Builder類中的所有set方法?

所以實例Example,它將成爲Example e = new Example().a(3).b.(3);代替Example e = new Example.Builder.a(3).b(3).build();


注:這本書表明,這種模式對於具有參數的長列表進行設置類。

+1

它對於生成不可變對象最有用。 – shmosel

+0

在一些複雜初始化的情況下,您的值應該已經設置。另外,使用'a()'和'b()'方法可以使'Example'對象變爲可變的。 – EpicPandaForce

+0

對於這種情況,生成器不是必需的,實際上使代碼更加冗長。 –

回答

8

建造者是一個模式,用於建造複合物對象。我不會把你的例子算作複雜的;實際上,構建器會添加大量不必要的代碼,而不僅僅是使用構造函數參數。

有,爲什麼你想使用一個建設者的幾個原因:

  • 構建複雜不可變對象。不可變的對象需要有最終的(或邏輯上最終的)字段,所以它們必須在施工時進行設置。

    假設您有N個字段,但您只想在某些用例中明確設置其中的一些字段。由於參數列表的長度變得越來越長,您將需要多達2^N的構造函數來涵蓋所有的情況 - 稱爲「伸縮」。構建器允許您爲可選參數建模:如果您不想設置該參數,請不要調用該設置器方法。

  • 允許參數意義的自我記錄。通過適當地指定setter方法,您可以一目瞭然地瞭解這些值的含義。

    這也有助於驗證您是否意外地顛倒了相同類型的參數,因爲您可以看到每個值的用途。

+0

關於命名方法命名允許推斷參數含義的好處。 – KevinO

+0

謝謝安迪,但是我是否正確地說你的第二點可以在沒有建造者模式的情況下實現呢? (與普通setters)所以基本上,只有當我想讓我的類不可變時,構建器模式纔有用。 –

+1

我的意思是,可以爲類指定「類似Builder的」設置方法(可以實現第二點),但這會使類變爲可變的。所以建設者模式是有益的**只有**如果我希望我的課是**不可變**與第二點的好處。 –

1

如果外部類中的字段是final的,那麼如果您想增量指定參數值,那麼構建器是必需的,因爲所有字段都必須在構造函數中初始化。

構建器內部類允許字段以增量方式初始化。

正如其他人指出的,這也適用於不可變對象。這些領域不需要是最終的;如果在外部班級沒有提供安裝人員,他們將會有效。

建造者可能比直接施工更有效地積累參數。考慮StringBuilder。它分配一個臨時緩衝區來累積部分結果。其構建中的「構建」操作是toString()

最後,可能有些東西在類的構造函數中是做不到的。如果您需要將值傳遞給超級構造函數,但該值不是構造函數參數的一部分,則可能無法執行,因爲必須先調用super(),並且可能無法創建參數( s)作爲super(...)調用中的簡單表達式。 BoxLayout讓人想起。您將JPanel傳遞給BoxLayout構造函數。您將佈局傳遞給構造函數JPanel。雞和雞蛋。而且這個代碼是不允許的,因爲this尚未構建。

class X extends JPanel { 
    X() { 
     super(new BoxLayout(this)); // Error: Cannot use "this" yet 
    } 
} 

幸運的是,JPanel不是不可變的;您可以在施工後設置佈局。

+0

否:如果外部類中的字段是最終字段,則可以通過顯式的構造函數參數提供它們。 –

+0

@AndyTurner - 抱歉,我的意思是增量建設。我會編輯更清晰。 – AJNeufeld

+0

@AndyTurner你是對的,但是本書建議這種模式只對那些不能簡單地作爲構造函數參數傳遞參數列表的對象有利。對於混淆抱歉,我應該在問題 –

1

理由是複雜的類。請注意0​​對象返回本身,所以可以做鏈接,如:

Example exp = Example.Builder().a(5).b(10).build(); 

Apache使用在某些情況下,這種方法允許不同值的增量設置。它還允許.build()方法根據需要對所有正確的值進行一些檢查以生成對象。

+0

謝謝凱文,這很有見地,+1! –