2010-04-06 17 views
8

लोड हो रहा है जैसा कि यहाँ दिखायावर्ग

public class TrueFalseQuestion implements Question{ 
    static{ 
     QuestionFactory.registerType("TrueFalse", "Question"); 
    } 
    public TrueFalseQuestion(){} 
} 

मैं कुछ वर्गों है बिना एक जावा में चलाने के लिए स्थिर प्रारंभ ब्लॉक मिलता है ...

public class QuestionFactory { 

static final HashMap<String, String > map = new HashMap<String,String>(); 

public static void registerType(String questionName, String ques) { 
    map.put(questionName, ques); 
    } 
} 



public class FactoryTester { 
    public static void main(String[] args) { 
     System.out.println(QuestionFactory.map.size()); 
     // This prints 0. I want it to print 1 
    } 
} 

मैं कैसे इतना है कि TrueFalseQuestion वर्ग बदल सकते हैं स्थैतिक विधि हमेशा चलती है ताकि जब मैं अपना मुख्य तरीका चलाता हूं तो मुझे 0 के बजाय 1 मिलता है? मैं मुख्य विधि में कोई बदलाव नहीं चाहता हूं।

मैं वास्तव में कारखाने के पैटर्न को लागू करने की कोशिश कर रहा हूं जहां सबक्लास कारखाने के साथ पंजीकृत है लेकिन मैंने इस प्रश्न के लिए कोड को सरल बना दिया है।

उत्तर

5

कारखाने के साथ TrueFalseQuestion कक्षा पंजीकृत करने के लिए, इसके स्थैतिक प्रारंभकर्ता को कॉल करने की आवश्यकता है। TrueFalseQuestion वर्ग के स्थिर प्रारंभकर्ता को निष्पादित करने के लिए, कक्षा को या तो संदर्भित करने की आवश्यकता है या इसे QuestionFactory.map.size() से पहले प्रतिबिंब द्वारा लोड करने की आवश्यकता है। यदि आप main विधि को छूने की विधि छोड़ना चाहते हैं, तो आपको इसे संदर्भित करना होगा या इसे QuestionFactory स्थिर प्रारंभकर्ता में प्रतिबिंब द्वारा लोड करना होगा। मुझे नहीं लगता कि यह एक अच्छा विचार है, लेकिन मैं सिर्फ आपके प्रश्न का उत्तर दूंगा :) यदि आपको QuestionFactory को उन सभी वर्गों के बारे में जानने की कोई बात नहीं है जो उन्हें बनाने के लिए Question लागू करते हैं, तो आप उन्हें सीधे संदर्भित कर सकते हैं या उन्हें लोड कर सकते हैं प्रतिबिंब। की तरह कुछ:

public class QuestionFactory { 

static final HashMap<String, String > map = new HashMap<String,String>(); 

static { 
    this.getClassLoader().loadClass("TrueFalseQuestion"); 
    this.getClassLoader().loadClass("AnotherTypeOfQuestion"); // etc. 
} 

public static void registerType(String questionName, String ques) { 
    map.put(questionName, ques); 
    } 
} 

यकीन map की घोषणा करें और निर्माण static ब्लॉक से पहले है। यदि आप Question के कार्यान्वयन के बारे में कोई जानकारी नहीं चाहते हैं, तो आपको उन्हें एक कॉन्फ़िगरेशन फ़ाइल में सूचीबद्ध करना होगा जो QuestionFactory द्वारा लोड हो जाता है। एकमात्र अन्य (संभावित रूप से पागल) जिस तरह से मैं इसे करने के बारे में सोच सकता हूं, Question को लागू करने वाले वर्गों के लिए पूरे क्लासपाथ को देखना होगा :) Question लागू किए गए सभी वर्गों को एक ही पैकेज से संबंधित होने की आवश्यकता होती है - ध्यान दें: मैं इस समाधान का समर्थन नहीं कर रहा हूँ;)

कारण मैं QuestionFactory स्थिर प्रारंभकर्ता में इस के किसी भी कर नहीं लगता कि है, क्योंकि TrueFalseQuestion तरह वर्गों को अपने स्वयं के स्थिर प्रारंभकर्ता कि QuestionFactory में कहता है, जो उस बिंदु पर है एक अपूर्ण रूप से निर्मित वस्तु, जो सिर्फ परेशानी के लिए पूछ रही है। एक कॉन्फ़िगरेशन फ़ाइल होने के कारण जो आपको QuestionFactory कक्षाओं को सूचीबद्ध करने के बारे में जानने के लिए बस सूचीबद्ध करता है, फिर उन्हें अपने कन्स्ट्रक्टर में पंजीकृत करना एक अच्छा समाधान है, लेकिन इसका मतलब यह होगा कि main विधि बदलना।

+0

संदर्भ के लिए, यह डिज़ाइन प्रश्न वर्गों के बारे में जानने वाले कारखाने की आवश्यकता से बचने के लिए आया था (यहां: http : //stackoverflow.com/questions/2582357/augment-the-factory-pattern-in-java)। –

+1

मैंने उस सवाल को नहीं देखा था। प्रश्न इंटरफ़ेस के कार्यान्वयन के बारे में कुछ जानने की आवश्यकता है, चाहे वह सीधे कारखाना है, या किसी प्रकार की कॉन्फ़िगरेशन फ़ाइल के माध्यम से। एकमात्र अन्य तरीका है, जैसा कि मैंने कहा था, कक्षा के सभी वर्गों के माध्यम से जा रहा है और देख रहा है कि क्या वे प्रश्न लागू करते हैं। ऊपर दिए गए मेरे उत्तर में एक अपूर्ण रूप से निर्मित कारखाने के बारे में चेतावनी भी ध्यान दें। यह अब काम कर सकता है, लेकिन भविष्य में वस्तु की स्थिति (या यहां तक ​​कि वर्तमान में, प्लेटफार्मों में) के बारे में कोई गारंटी नहीं है। –

6

आप कॉल कर सकते हैं:

Class.forName("yourpackage.TrueFalseQuestion"); 

इस आप वास्तव में बिना स्पर्श किए वर्ग लोड होगा, और स्थिर प्रारंभकर्ता ब्लॉक निष्पादित करेंगे।

+0

मैं इस विधि को कहां कहूं? प्रत्येक वर्ग एक अलग फ़ाइल में है। – randomThought

+0

आपको वास्तव में चलाने के लिए 'TrueFalseQuestion' प्रारंभकर्ता की आवश्यकता होने से पहले। आपके उदाहरण में - मुख्य विधि की शुरुआत में – Bozho

+0

क्या कोई तरीका है कि मैं मुख्य विधि में कुछ भी बदले बिना इसे प्राप्त कर सकता हूं क्योंकि इससे मुख्य वर्ग में इस वर्ग की निर्भरता पैदा होगी जो मैं टालना चाहता हूं। – randomThought

3

कक्षा के लिए स्थिर प्रारंभकर्ता को तब तक निष्पादित नहीं किया जा सकता है जब वर्ग कभी लोड नहीं होता है।

तो आपको या तो सभी सही वर्गों को लोड करने की आवश्यकता है (जो कठिन होगा, क्योंकि आप उन्हें संकलित समय पर सभी नहीं जानते हैं) या स्थिर प्रारंभकर्ता के लिए आवश्यकता से छुटकारा पाएं।

उत्तरार्द्ध करने का एक तरीका ServiceLoader का उपयोग करना है।

ServiceLoader के साथ आप बस META-INF/services/package.Question में एक फ़ाइल डालते हैं और सभी कार्यान्वयनों को सूचीबद्ध करते हैं। आपके पास एकाधिक ऐसी फ़ाइलें हो सकती हैं, एक प्रति .jar फ़ाइल। इस तरह आप अपने मुख्य कार्यक्रम से अलग अतिरिक्त Question कार्यान्वयन आसानी से भेज सकते हैं।

QuestionFactory में आप तो बस एक ServiceLoader, जो Iterable<Question> लागू करता है और इस तरह इस्तेमाल किया जा सकता प्राप्त करने के लिए ServiceLodaer.load(Question.class) उपयोग कर सकते हैं:

for (Question q : ServiceLoader.load(Question.class)) { 
    System.out.println(q); 
} 
2

आदेश स्थिर initializers चलाने के लिए, वर्गों लोड करने के लिए की जरूरत है। ऐसा होने के लिए, या तो आपकी "मुख्य" कक्षा कक्षाओं पर (प्रत्यक्ष या परोक्ष रूप से) निर्भर होनी चाहिए, या इसे सीधे या अप्रत्यक्ष रूप से उन्हें गतिशील रूप से लोड किया जाना चाहिए; जैसे Class.forName(...) का उपयोग कर।

मुझे लगता है कि आप निर्भरता से बचने की कोशिश कर रहे हैं आपके स्रोत कोड में एम्बेडेड। इसलिए स्थैतिक निर्भरता अस्वीकार्य हैं, और Class.forName(...) पर हार्ड-कोडित क्लास नामों के साथ कॉल अस्वीकार्य भी हैं।

  • कुछ पैकेज में संसाधन नाम से अधिक पुनरावृति करने के लिए कुछ गन्दा कोड लिखें, और फिर Class.forName(...) का उपयोग उन संसाधनों कि अपनी कक्षाओं की तरह लग रहे लोड करने के लिए:

    यह आपको दो विकल्प छोड़ देता है। यह दृष्टिकोण मुश्किल है यदि आपके पास जटिल क्लासपाथ है, और असंभव है यदि आपके प्रभावी क्लासपाथ में एक दूरस्थ URL (उदाहरण के लिए) के साथ URLClassLoader शामिल है।

  • एक फ़ाइल (उदा। एक क्लासलोडर संसाधन) बनाएं जिसमें आपके द्वारा लोड किए जाने वाले वर्ग नामों की एक सूची है, और फ़ाइल को पढ़ने के लिए कुछ सरल कोड लिखें और प्रत्येक को लोड करने के लिए Class.forName(...) का उपयोग करें।

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