2015-07-09 7 views
11

से रिटर्न बाल वर्ग मेरे पास एक बिल्डर वर्ग है जो डेज़ी-चेनिंग के लिए अनुमति देने के लिए सबसे अधिक तरीकों से खुद को लौटाता है। इस काम को बाल वर्गों के साथ बनाने के लिए, मैं चाहता हूं कि माता-पिता बच्चे के उदाहरण वापस लौट सकें ताकि बच्चे के तरीके अंत तक श्रृंखला के लिए उपलब्ध हों।अभिभावक वर्ग

public class BaseBuilder<T extends BaseBuilder<T>> { 
    public T buildSomething() { 
     doSomeWork(); 
     /* OPTION #1: */ return this;  // "Type mismatch: cannot convert from BaseBuilder<T> to T" 
     /* OPTION #2: */ return T.this; // "Type mismatch: cannot convert from BaseBuilder<T> to T" 
     /* OPTION #3: */ return (T) this; // "Type safety: Unchecked cast from SqlBuilder<T> to T" 
    } 
} 

public class ChildBuilder extends BaseBuilder<ChildBuilder> {} 

विकल्प # 1 और # संकलन त्रुटियों में 2 परिणाम, और # 3 विकल्प के लिए एक चेतावनी (यद्यपि कि @SuppressWarnings("unchecked") साथ दबा दिया जा सकता है)। क्या यहां एक बेहतर दृष्टिकोण है? मैं बेसबिल्डर को चाइल्डबिल्डर को सुरक्षित रूप से कैसे घटा सकता हूं? विधि की घोषणा T वापस जाने के लिए की

+0

यह सार्वजनिक वर्ग बेसबिल्डर नहीं होना चाहिए सामान्य प्रकार एक ही कक्षा का जिक्र क्यों कर रहा है? => चाइल्डबिल्डर बेसबिल्डर <चाइल्डबिल्डर> – 6ton

+0

@ 6ton बढ़ाता है, क्योंकि केवल वैध जेनेरिक प्रकार बेसबिल्डर का उत्तराधिकारी हैं। उदाहरण के लिए 'बेसबिल्डर ' समझ में नहीं आता है। –

+0

अभी भी सुनिश्चित नहीं है कि आपको हस्ताक्षर की आवश्यकता क्यों है - मैंने औचित्य – 6ton

उत्तर

6

घोषणा ChildBuilder extends BaseBuilder<ChildBuilder> किसी तरह एक कोड गंध को इंगित करता है और सूखी का उल्लंघन हो रहा है। इस उदाहरण में BaseBuilder केवल ChildBuilder के साथ parametrized किया जा सकता है और कुछ भी नहीं, तो यह अनावश्यक होना चाहिए।

मैं इस बात पर पुनर्विचार करना चाहूंगा कि मैं वास्तव में इसे अधिक आर्किटेक्चर करना चाहता हूं और मैं बाल बिल्डरों से सभी विधियों को BaseBuilder में डालने का प्रयास करूंगा। तो मैं चेनिंग का समर्थन करने वाले सभी तरीकों से this वापस कर सकता हूं।

यदि मुझे अभी भी लगता है कि मुझे बिल्डर विधियों के विशिष्ट समूहों को अपने वर्गों में अलग करके लाभ होगा, तो मैं संरचना को वरीयता दूंगा, क्योंकि केवल कोड पुन: उपयोग के लिए विरासत लागू करने की अनुशंसा नहीं की जाती है।

मान लीजिए हम BaseBuilder की दो उपवर्गों है:

class BuilderA extends BaseBuilder<BuilderA> { 
    BuilderA buildSomethingA() { return this; } 
} 

class BuilderB extends BaseBuilder<BuilderB> { 
    BuilderB buildSomethingB() { return this; } 
} 

क्या होगा अगर जरूरत श्रृंखला buildSomethingA और buildSomethingB की तरह करने के लिए पैदा होता है:

builder.buildSomething().buildSomethingA().buildSomethingB(); 

हम ले जाए बिना यह करने के लिए सक्षम नहीं होगा BaseBuilder के लिए subclass विधियों; लेकिन कल्पना करें कि BuilderC भी है जिसके लिए वे विधियां समझ में नहीं आती हैं और उन्हें से विरासत में नहीं मिला जाना चाहिए।

यदि हम फिर भी इन दो विधियों को सुपरक्लास में ले जाते हैं, और अगली बार तीन अन्य विधियों और अगली बार ... हम पूरे पदानुक्रम के 9 0% कर्तव्यों के लिए जिम्मेदार सुपरक्लस के साथ समाप्त होंगे जैसे:

builder.buildSomething1().buildSomething2() 
    .builderA() 
     .buildSomethingA1().buildSomethingA2() 
    .end() 
    .buildSomething3() 
    .builderB() 
     .buildSomethingB() 
    .end(); 

यहाँ end() रिटर्न builder उदाहरण ताकि आप अपने तरीकों में से श्रृंखला अधिक कर सकते हैं या एक नया उप-बिल्डर शुरू:

if ((this instanceof BuilderB) && !flag1 && flag2) { 
    ... 
} else if ((this instanceof BuilderC) && flag1 && !flag2 && thing != null) { 
    ... 
} else if ... 

समाधान मैं और अधिक की तरह की तरह एक डीएसएल है।

इस तरह (उप) बिल्डर्स जो कुछ भी चाहते हैं उससे प्राप्त कर सकते हैं (अन्यथा उन्हें केवल BaseBuilder का विस्तार करना होगा) और उनके स्वयं के सार्थक पदानुक्रम या रचनाएं हो सकती हैं। के बाद से निम्नलिखित वर्ग संकलन होगा # 3 विकल्प में

+0

के साथ एक उत्तर पोस्ट किया धन्यवाद, आप कुछ अच्छे अंक बनाते हैं। 'अंत()' सुझाव एक अच्छा विचार है! –

0

बजाय - घोषित यह BaseBuilder वापस जाने के लिए:

public BaseBuilder buildSomething() { 
... 

T के बाद से BaseBuilder फैली - लेकिन अभी भी संकलन समय के दौरान ज्ञात नहीं है, मुझे विश्वास है कि है कि सबसे अच्छा समझौता आप कर सकते हैं कर।

यदि आप जानते हैं (संकलन समय के दौरान) आप किस प्रकार टाइप कर रहे हैं तो आप इसे वापस कर सकते हैं, लेकिन यदि नहीं - और आपको डाउनकास्ट करना होगा - आप "Type safety: Unchecked cast प्राप्त करेंगे और यदि आप यह साबित कर सकते हैं कि डाउनकास्ट मान्य है जो SuppressWarnings पर बिल्कुल ठीक है।

संबंध में जोश ब्लोच के wise words देखें।

1

एक संभावना इस तथ्य का उपयोग करना है कि जावा कॉन्वेंट रिटर्न प्रकारों का समर्थन करता है। उदाहरण के लिए, इस कोड को कानूनी है:

class BaseBuilder { 
    BaseBuilder buildSomething() { (...) return this; } 
} 

class ChildBuilder extends BaseBuilder { 
    @Override // Notice the more specific return type 
    ChildBuilder buildSomething() { (...) return this; } 
} 

void main() { 
    BaseBuilder x = new BaseBuilder().buildSomething().anotherOperation(); 
    ChildBuilder y = new ChildBuilder().buildSomething().anotherOperation(); 
} 

अन्यथा, विकल्प # 3 एक ही रास्ता वास्तव में प्राप्त करने के लिए आप क्या चाहते है।

@SuppressWarnings("unchecked") // Ugly. 
class Base<T extends Base<T>> { // Ugly. 
    public T alpha() { return (T)this; } 
    public T delta() { return (T)this; } 
} 

class Child extends Base<Child> { // Clean. 
    // No need to override/redefine alpha() and delta() in child. 
    public Child gamma() { return this; } 
} 

void main(String[] args) { 
    Child x = new Child(); 
    x.alpha().gamma(); // This works because alpha() returns Child. 
} 
-1

IMO आप आधार बिल्डर हस्ताक्षर की जरूरत है BaseBuilder<T extends BaseBuilder<T>> परिवर्तित करने की: यह सुपर क्लास तरीकों सीधे एक उपवर्ग प्रकार वापस जाने के लिए इतना है कि आप उपवर्ग तरीकों आह्वान कर सकते हैं अनुमति देता है।

मैं टी कल्पना प्रकार का उल्लेख करने के लिए अभी भी ChildBuilder में BaseBuilder<T extends ComplexObject> और ChildBuilder extends BaseBuilder<MoreComplexObject>

अधिभावी तरीकों निर्माण किया जा रहा काम करता है - आप this लौट आते हैं। निर्माण विधि से आप T

public class BaseBuilder<T extends ComplexObject> { 
    public BaseBuilder<T> withComplexThing() { 
     return this; 
    } 

    public T build() { 
    } 

} 

public class ChildBuilder extends BaseBuilder<MoreComplexObject> { 
    public ChildBuilder withComplexThing() { 
     return this; 
    } 

    public MoreComplexObject build() { 
    } 
} 
-1

लौट यह ठीक है, सिर्फ चेतावनी को दबाने है।

आप एक शुद्धतावादी होना चाहिए, तो यहां एक समाधान है:

abstract public class BaseBuilder<T...> 
{ 
    abstract protected T getThis(); 

    public T buildSomething() 
     ... 
      return getThis(); 


... 

public class ChildBuilder extends BaseBuilder<ChildBuilder> 
{ 
    @Override 
    protected ChildBuilder getThis(){ return this; } 
} 

मैं पुनरावर्ती बाध्य खाई करने के लिए सलाह देते हैं; यह ज्यादातर बेकार है। बस प्रकार परिवर्तनीय This नाम दें।

public class BaseBuilder<This> 
1

कास्ट सुरक्षित नहीं है (यह डेवलपर जिम्मेदारी है):

public class ChildBuilder extends BaseBuilder<FakeBuilder> {} 
               ^^^^^^^^^^^ 

एक आम समाधान उनके this के लिए उपवर्गों पूछना है:

public abstract class BaseBuilder<T extends BaseBuilder<T>> { 
    protected abstract T getThis(); 
    public T buildSomething() { 
    return getThis(); 
    } 
} 

public class ChildBuilder extends BaseBuilder<ChildBuilder> { 
    @Override 
    protected ChildBuilder getThis() { 
    return this; 
    } 
} 
संबंधित मुद्दे