2012-06-10 38 views
17

我正在解決一個問題,其中有Foo的幾個實現,並伴隨着幾個FooBuilder的實現。儘管Foo共享了需要設置的幾個常用變量,但它們也具有不同的變量,這些變量需要各自的FooBuilder來實現某些特定的功能。爲了簡潔,我想有FooBuilder的制定者使用方法鏈接,如:Java:返回超類方法簽名中的子類

public abstract class FooBuilder { 
    ... 

    public FooBuilder setA(int A) { 
    this.A = A; 
    return this; 
    } 

    ... 
} 

public class FooImplBuilder extends FooBuilder{ 
    ... 
    public FooImplBuilder setB(int B) { 
    this.B = B; 
    return this; 
    } 
    public FooImplBuilder setC(int C) { 
    this.C = C; 
    return this; 
    } 
    ... 
} 

等等,有幾個不同的FooBuilder實現。這在技術上是做我想要的,但是,這種方法對方法鏈接執行時方法調用的順序很敏感。以下方法有未定義的編譯錯誤:

someFoo.setA(a).setB(b)... 

要求開發人員考慮鏈中方法調用的順序。爲了避免這種情況,我想讓FooBuilder中的setter返回實際的實現子類。但是,我不知道如何做到這一點。什麼是最好的方法?

+0

此外,它要麼強制鑄造無處不在,要麼阻止你在需要時改變超類的屬性。 –

回答

13

這是一個很好的問題和一個真正的問題。

在Jochen的回答中提到,在Java中處理它最簡單的方法可能涉及使用泛型。

有問題的一個很好的討論,並在Using Inheritance with Fluent Interfaces此博客條目,它結合了仿製藥與建設者子類重寫一個getThis()方法的定義來解決的總是返回正確的類的建設者問題的一個合理的解決方案。

+1

我喜歡這個解決方案。比我提出的更精細,但也更靈活,最終優雅。 – Jochen

+0

只是爲了總結鏈接的博客,並刪除單獨的構建器:'公共抽象類X > {public int a; public B setA(int foo){this.a = foo; return getThis();} public abstract B getThis();} public class Y extends X {public int b; public Y setB(int bar){this.b = bar; return getThis();} public Y getThis(){return this;}} ' –

3

泛型可能是去這裏的路。

如果聲明組A()是這樣的(僞代碼)

<T> T setA(int a) 

編譯器應該能夠找出真正的類型,如果沒有,你可以在代碼給提示像

obj.<RealFoo>setA(42) 
+3

+1:在http://egalluzzo.blogspot.com/2010/06/using-inheritance-with-fluent.html有一個很好的描述這個策略 –

+0

編譯器*不能*弄清楚鏈接的類型方法調用,並且重複每個方法調用的類型幾乎不是一種選擇。 – meriton

+0

@DonRoby:你爲什麼不把它作爲答案?它恰好是一個比Jochen更好的解決方案,肯定會得到滿意的答覆;-) – meriton

1

找到了this excellent answer我現在正在分享它。

public class SuperClass<I extends SuperClass> 
{ 
    @SuppressWarnings("unchecked") // If you're annoyed by Lint. 
    public I doStuff(Object withThings) 
    { 
     // Do stuff with things. 
     return (I)this ; // Will always cast to the subclass. Causes the Lint warning. 
    } 
} 

public class ImplementationOne 
extends SuperClass<ImplementationOne> 
{} // doStuff() will return an instance of ImplementationOne 

public class ImplementationTwo 
extends SuperClass<ImplementationTwo> 
{} // doStuff() will return an instance of ImplementationTwo