2016-01-19 89 views
0

我想在一些即將開展的工作中使用構建器模式,這種工作在層次結構中有幾個類。基類將有至少9個字段開始,並且各個子類可以在每個字段中添加2-4個個更多個字段。這將非常迅速地失控,建設者模式對我來說正是因爲這個原因。我初步接觸了書籍和文章中的建築模式。他們很有幫助,但對如何擴展這種模式一無所知。我試圖自己實現這一點,但是我遇到了每個子類的構造函數的麻煩,因爲我沒有得到如何將構建器中收集的數據傳遞給超類。我看了一些答案,這是我發現的。其中如何與一個抽象的助洗劑延伸一個抽象類給出一個例子使用抽象構建器vs具體類和具體構建器擴展抽象類

這一個是從SO 24243240。它也是基於這個blog post

public abstract class AbstractA { 
protected String s; 
protected int i; 
protected AbstractA() { 
} 
protected abstract static class ABuilder<T extends AbstractA, B extends ABuilder<T,B>> { 
    protected T object; 
    protected B thisObject; 
    protected abstract T getObject(); //Each concrete implementing subclass overrides this so that T becomes an object of the concrete subclass 
    protected abstract B thisObject(); //Each concrete implementing subclass builder overrides this for the same reason, but for B for the builder 
    protected ABuilder() { 
     object = getObject(); 
     thisObject = thisObject(); 
    } 
    public B withS(String s) { 
     object.s = s; 
     return thisObject; 
    } 
    public B withI(int i) { 
     object.i = i; 
     return thisObject; 
    } 
    public T build() { 
     return object; 
    } 
} 
} 


public final class ConcreteA extends AbstractA { 
    private String foo; 
    protected ConcreteA() { 
    } 
    public static final class Builder extends AbstractA.ABuilder<ConcreteA,Builder> { 
     @Override protected ConcreteA getObject() { 
      return new ConcreteA(); 
     } 
     @Override protected Builder thisObject() { 
      return this; 
     } 
     public Builder() { 
     } 
     public Builder withFoo(String foo) { 
      object.foo = foo; 
      return this; 
     } 
    } 
} 

然後在客戶端代碼,它看起來像......

ConcreteA baz = new ConcreteA.Builder().withFoo("foo").withS("bar").withI(0).build(); 

我喜歡這個例子,因爲它可以讓你輕鬆地擴展這些類,但它似乎也對我來說,這違背使用構建器模式的目的,因爲方法withS(String s)withI(int i)的行爲很像setter方法。此外,此方法將基類和構建器類的字段保留爲受保護而非私有。

這裏有一個從SO 17164375

public class NutritionFacts { 

    private final int calories; 

    public static class Builder<T extends Builder> { 

     private int calories = 0; 

     public Builder() {} 

     public T calories(int val) { 
      calories = val; 
      return (T) this; 
     } 

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

    protected NutritionFacts(Builder builder) { 
     calories = builder.calories; 
    } 
} 

public class GMOFacts extends NutritionFacts { 

    private final boolean hasGMO; 

    public static class Builder extends NutritionFacts.Builder<Builder> { 

     private boolean hasGMO = false; 

     public Builder() {} 

     public Builder GMO(boolean val) { 
      hasGMO = val; 
      return this; 
     } 

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

    protected GMOFacts(Builder builder) { 
     super(builder); 
     hasGMO = builder.hasGMO; 
    } 
} 

我喜歡這一個看似更緊密地堅持由喬希布洛赫描述的建設者模式,它也可以讓你簡單地生成器傳遞到構造函數需要類實例化。這將是一個不錯的方式之前做的建設者內一些驗證實例在調用build()的對象。與此同時,雖然,這個例子說明如何擴展與具體類生成器模式,當你做到這一點自帶的擴展的具體類(如不一致的接口,繼承方法的所有污穢的潛力,其可以破壞的狀態你的對象,等)

所以我的問題是沒有實現與一個抽象的生成器,也可以讓你在構造函數的基類的引用到建築商通過一個抽象類的方法嗎?喜歡的東西:

public abstract BaseClass { 
// various fields go here 
... 
    public abstract Builder<T extends BaseClass, B extends Builder<T,B>> { 
    // add chaining methods here 
    ... 
    public T build() { 
     if (isValid()) return new T(this); 
     else Throw new IllegalArgumentException("Invalid data passed to builder."); 
    } 
    } 
public BaseClass(Builder builder) { 
    // set fields of baseclass here 
} 
} 

我知道你不能實例化一個對象,我在這裏顯示的方式,但有一些其他的方式來做到這一點我的意思嗎?這可能是工廠會去的地方嗎?也許我只是對建設者模式有一些錯誤的假設。 :)如果是這樣,是否有更好的方向?

回答

0

你的第一個例子是不壞,但我不認爲這是你在找什麼。

我還是你想要什麼有點不確定,但看到你的例子並不爲你工作,我想我給你一個或兩個我自己的。 :)

class ParentBuilder{ 
    public ConcreteParent build(){ 
     ConcreteParent parent = new ConcreteParent(); 
     parent.setFirst(1); 
     parent.setSecond(2); 
     parent.setThird(3); 
     return parent; 
    } 
} 

class ChildBuilder{ 
    public ConcreteChild build(ParentBuilder parentBuilder){ 
     ConcreteParent parent = parentBuilder.build(); 
     ConcreteChild child = new ConcreteChild(); 
     child.setFirst(parent.getFirst()); 
     child.setSecond(parent.getSecond()); 
     child.setThird(parent.getThird()); 
     child.setFourth(4); //Child specific value 
     child.setFifth(5); //Child specific value 
     return child; 
    } 
} 

任何新的類型,將有其自己的建設者,採取其父母的建設者。 正如你所看到的,它類似於:

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

    protected NutritionFacts(Builder builder) { 
     calories = builder.calories; 
    } 

在你的例子中。

但是,這種情況也會隨着變量和子類的數量的增加而迅速失控。

另一種方法是使用dynanic變量,看看這個:http://martinfowler.com/apsupp/properties.pdf Martin Fowler寫了一篇很棒的文章,指出所有的優點和缺點。

不管怎麼說,這是我的第二個例子:

public class Demo { 

    public static void main(String[] args) { 
     ConcreteBuilder builder = new ConcreteBuilder(); 
     Concrete concrete = builder.with("fourth", "valueOfFourth").build(); 
     for(String value : concrete.getAttributes().values()) 
      System.out.println(value); 
    } 
} 

class ConcreteBuilder{ 
    private Concrete concrete; 

    public ConcreteBuilder(){ 
     concrete = new Concrete(); 
    } 

    public ConcreteBuilder with(String key, String value){ 
     concrete.getAttributes().put(key, value); 
     return this; 
    } 
    public Concrete build(){ 
     return concrete; 
    } 
} 

class Concrete{ 
    private HashMap<String, String> attributes; 

    public Concrete(){ 
     attributes = new HashMap<>(); 
    } 

    public HashMap<String, String> getAttributes(){ 
     attributes.put("first", "valueOfFirst"); 
     attributes.put("second", "valueOfSecond"); 
     attributes.put("third", "valueOfThird"); 
     return attributes; 
    } 
} 

這裏神奇的是,你(可能)不再需要所有這些子類。 如果這些子類的行爲沒有改變,但只有它們的變量,那麼使用這樣的系統應該沒問題。 我強烈建議你閱讀Martin Fowler關於這個主題的文章,雖然有好的地方和不好的地方可以做到這一點,但我認爲這是一個很好的地方。

我希望這會讓你更接近答案,祝你好運。 :)

+0

我知道這是有點晚,但你的答案!當時我的主管告訴我完全避免了構建模式,因爲Beans需要有getter和setter方法。我很天真,希望實施一個「聰明」的解決方案。 – maester999