2010-05-25 31 views
19

के निर्माता के बाद एक विधि चल रहा है मान लीजिए मैं एक जावा वर्गकिसी भी व्युत्पन्न वर्ग

abstract class Base { 
    abstract void init(); 
    ... 
} 

है और मुझे पता है कि हर व्युत्पन्न वर्ग init() कॉल करने के लिए के बाद यह निर्माण किया है होगा। मैं, ज़ाहिर है, बस इसे व्युत्पन्न वर्ग 'कंस्ट्रक्टर्स में कह सकते हैं:

class Derived1 extends Base { 
    Derived1() { 
     ... 
     init(); 
    } 
} 

class Derived2 extends Base { 
    Derived2() { 
     ... 
     init(); 
    } 
} 

लेकिन यह टूटता नहीं बल्कि बुरी तरह सिद्धांत "अपने आप को दोहराने नहीं है" (और वहाँ Base के कई उपवर्गों होने जा रहे हैं)। बेशक, init() कॉल Base() कन्स्ट्रक्टर में नहीं जा सकता है, क्योंकि इसे बहुत जल्दी निष्पादित किया जाएगा।

कोई भी विचार इस समस्या को कैसे बाधित करना है? मुझे स्कैला समाधान भी देखने में काफी खुशी होगी।

अद्यतन:

interface Maker<T extends Base> { 
    T make(); 
} 

class Base { 
    ... 
    static <T extends Base> T makeAndInit(Maker<T> maker) { 
     T result = maker.make(); 
     result.init(); 
     return result; 
    } 
} 

अद्यतन 2: इस प्रश्न का मूल रूप से है "कैसे आप निर्माताओं के लिए टेम्पलेट विधि का उपयोग करते हैं" यहाँ फैक्टरी विधि दृष्टिकोण की एक सामान्य संस्करण है? और जवाब लगता है, "आप कर सकते हैं, लेकिन यह एक बुरा विचार है"। तो मैं इसके बजाय एक टेम्पलेट फैक्टरी (टेम्पलेट विधि + सार फैक्टरी) कर सकते हैं।

+2

यदि init() आधार वर्ग में सार है, तो प्रत्येक व्युत्पन्न वर्ग को इसे क्यों कॉल करना पड़ता है? क्या होता है अगर वे नहीं करते? –

+1

आप 'बेस' कक्षा के निर्माता में init() क्यों नहीं कहते हैं? – aioobe

+0

@ स्किप हेड: वे ठीक से शुरू नहीं किए जाएंगे। –

उत्तर

10

init() में क्या होता है? ऐसा लगता है कि एक बेहतर डिजाइन पूरी तरह से विधि को खत्म कर सकता है, या कम से कम उस आवश्यकता को आराम कर सकता है जो उप-वर्ग के निर्माता के बाद निष्पादित करता है। सुनिश्चित करें कि init() कन्स्ट्रक्टर पूर्ण होने से पहले ऑब्जेक्ट को निर्माणाधीन किसी भी अन्य थ्रेड के लिए दृश्यमान नहीं बनाता है, क्योंकि यह समवर्ती बग बनाता है।

एक (बदसूरत) विकल्प के रूप में एक सार विधि एक छद्म निर्माता के रूप में उप-वर्गों द्वारा कार्यान्वित किया जा सकता है:

abstract class Base { 
    Base() { 
    ctor(); 
    init(); 
    } 
    abstract void ctor(); 
    abstract void init(); 
} 
+0

असल में, यह मेरे लिए अन्य समाधानों से बेहतर दिखता है (बदसूरत, लेकिन विकल्प से कम बदसूरत)। –

10

इससे बचें। यदि आप ऐसा करते हैं, तो आपकी DerivedX कक्षा को विस्तारित करने वाली कोई भी कक्षा init() पर कॉल करने का निर्णय ले सकती है जिससे इस प्रकार वस्तु को असंगत स्थिति में छोड़ दिया जा सकता है।

एक दृष्टिकोण init() विधि को आपकी कक्षा के ग्राहकों द्वारा मैन्युअल रूप से लागू करने देना है। initialized फ़ील्ड लें, और IllegalStateExcepion फेंक दें यदि किसी भी विधि को प्रारंभिक आवश्यकता की आवश्यकता है तो उसे बिना बुलाया जाता है।

एक बेहतर दृष्टिकोण कंस्ट्रक्टर्स के बजाय एक स्थिर कारखाने विधि का उपयोग करने होगा:

public Derived2 extends Base { 
    public static Derived2 create() { 
     Derived2 instance = new Dervied2(); 
     instance.init(); 
     return instance; 
    } 
} 

अद्यतन: यदि आप अपने अद्यतन में पता चलता है कि आप एक स्थिर कारखाने विधि के लिए Builder पारित कर सकते हैं, जो init() कॉल करेंगे उदाहरण पर। यदि आपके उप-वर्ग कम हैं, तो मुझे लगता है कि यह एक अतिसंवेदनशील है, हालांकि।

+1

यह अभी भी मुझे 'व्युत्पन्न 1', 'व्युत्पन्न 2', आदि के लिए कारखाने विधि में अलग-अलग 'init()' कॉल करने के साथ छोड़ देता है। –

+0

हां, आपका निर्माता संस्करण एक अच्छा जोड़ा है। – Bozho

+0

प्रश्न से अपना अंतिम वाक्य पुन:: "(और बेस के कई उप-वर्ग होने जा रहे हैं)"। जिज्ञासा से –

6

Bozho के recommendation के अतिरिक्त, एक एप्लिकेशन कंटेनर कार्य के लिए उत्कृष्ट है।

javax.annotation.PostConstruct एनोटेशन के साथ init() विधि को चिह्नित करें और निर्भरता इंजेक्शन समाप्त होने के बाद सही ढंग से कॉन्फ़िगर किया गया ईजेबी या स्प्रिंग कंटेनर विधि निष्पादित करेगा, लेकिन ऑब्जेक्ट का उपयोग एप्लिकेशन द्वारा किया जा सकता है।

एक उदाहरण विधि:

@PostConstruct 
public void init() { 
    // logic.. 
} 

आप init() विधि में उदाहरण के लिए फ़ाइलों को व्यवस्था करने के लिए संसाधनों को खोल सकते हैं एक उद्यम आवेदन में। यह प्रारंभिक अपवाद फेंक सकता है और इसे कन्स्ट्रक्टर से नहीं बुलाया जाना चाहिए।

+0

। किसी को कन्स्ट्रक्टर से क्यों नहीं फेंकना चाहिए, बल्कि 'init()' तरीकों से क्यों? – Tarrasch

1

या का उपयोग वसंत ... आप <beans default-init-method="init"> कर सकते हैं, Default initialization and destroy methods देखते हैं। Derived1 निर्माता/शरीर से पहले

trait RunInit { 
    def init():Unit 
    init() 
} 

class Derived1 extends Base with RunInit { 
    def init() = println("INIT'ing!") 
} 

यह init चलेगा():

1

आप किसी कारण से कारखानों का उपयोग करने के प्रतिकूल हैं, तो आप निम्नलिखित चाल इस्तेमाल कर सकते हैं।

+1

समस्या यह है कि मैं 'init' को 'derived1' कन्स्ट्रक्टर/बॉडी _after_ चलाने के लिए चाहता हूं। –

2

यदि जावा था, तो हम जंगली में इन सभी init() विधि कॉल नहीं देख पाएंगे।

"कुछ के साथ चारों ओर बाल निर्माता" - जो शुद्ध जावा में नहीं किया जा सकता है। बहुत बुरा, क्योंकि बहुत दिलचस्प अनुप्रयोग हो सकते हैं, खासकर अज्ञात वर्ग + उदाहरण प्रारंभिक ब्लॉक के साथ।

कारखाना और कंटेनर - वे उपयोगी हो सकते हैं जब मूल new नौकरी नहीं करता है; लेकिन यह मामूली और उबाऊ है, और अज्ञात वर्गों के साथ काम नहीं करेगा।

संबंधित मुद्दे