2012-09-13 38 views
3

将构建器实例存储在其构建的实例中是否被认为是一种好的做法?问题是,当我需要创建一个与我已有的非常类似的对象时,我经常发现自己处于这种情况。可以推测这个物体有很多8-10个领域。通常,对于一个可变对象,我只会使用setter。不可变对象构建器

例如,让我们的经典布洛赫的NutricionFacts例如:

public class NutritionFacts { 
    private final int servingSize; 
    private final int servings; 
    private final int calories; 
    private final int fat; 
    private final int sodium; 
    private final int carbohydrate; 

    private final Builder builder; 

    public static class Builder { 
     // Required parameters 
     private final int servingSize; 
     private final int servings; 
     // Optional parameters - initialized to default values 
     private int calories = 0; 
     private int fat = 0; 
     private int carbohydrate = 0; 
     private int sodium = 0; 

     public Builder(int servingSize, int servings) { 
      this.servingSize = servingSize; 
      this.servings = servings; 
     } 
     public Builder calories(int val) 
     { calories = val; return this; } 
     public Builder fat(int val) 
     { fat = val; return this; } 
     public Builder carbohydrate(int val) 
     { carbohydrate = val; return this; } 
     public Builder sodium(int val) 
     { sodium = val; return this; } 

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

    private NutritionFacts(Builder builder) { 
     servingSize = builder.servingSize; 
     servings = builder.servings; 
     calories = builder.calories; 
     fat = builder.fat; 
     sodium = builder.sodium; 
     carbohydrate = builder.carbohydrate; 
     this.builder = builder; 
    } 
} 

我已经修改了一点,所以如果我想在未来simmiliar副本我可以访问Builder实例?

您认为如何?

+0

或者你可能有一个克隆方法。 –

+1

@dystroy克隆不是一个好选择,因为类不可变,克隆将与第一个实例完全相同,但无法修改。 –

+1

ante的答案可能是正确的道路,并且说,不,你不应该在建造的类中引用构建器。不可变对象的一点是它是不可变的。通过引用一个可到达的可变对象,可以让你设想不可变对象是可变的。如果两个线程开始使用相同的构建器来做不同的事情会怎么样?你以前线程安全的不可变对象现在不是线程安全的。 –

回答

5

如果您重新使用Builder来构建第二个实例,会发生什么情况?然后,第一个实例中的Builder将生成类似于第二个实例的实例。可能不是你所期望的。

我会建议一个选项来创建一个模板实例Builder

public Builder(NutritionFacts template) { 
     this.servingSize = template.getServingSize(); 
     ... 
    }