2010-04-30 14 views
7

के माध्यम से गैर-सार्वजनिक प्रकार का निर्यात करना क्या होगा यदि मेरे पास गैर-सार्वजनिक प्रकार और विधियों के सेट को जोड़कर कुछ फैक्ट्री विधियां हैं जो इस गैर-सार्वजनिक प्रकार के चर प्रदान करती हैं? नेटबीन में शीर्षक चेतावनी संदेश के साथ इसका परिणाम।सार्वजनिक एपीआई

परिणामस्वरूप सार्वजनिक एपीआई में विधियों के केवल दो जोड़ी सेट होंगे। इसका कारण है कि मेरे प्रकार के पदानुक्रम को सील कर दिया जाए (जैसे स्कैला में सील कक्षाएं) और उपयोगकर्ताओं को कारखाने के तरीकों के माध्यम से इन प्रकारों को तुरंत चालू करने दें। तो हम कुछ अर्थ में डीएसएल प्राप्त करते हैं।

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

NumberSet singleton(int value); 
NumberSet range(int form, int to); 
NumberSet list(NumberSet ... components); 

और बनाने अनुसूची वस्तु के लिए कुछ तरीके::

Schedule everyHour(NumberSet minutes); 
Schedule everyDay(NumberSet minutes, NumberSet hours); 

उपयोगकर्ता केवल ढंग से उन्हें इस्तेमाल कर सकते हैं:

Schedule s = Schedule.everyDay(singleton(0), list(range(10-15), singleton(8))); 

वर्ग अनुसूची में हम बाधाओं के लिए कुछ कारखाने में तरीकों बनाने क्या यह बुरा विचार है?

उत्तर

9

विचार स्वयं ही ध्वनि है। लेकिन यह काम नहीं करेगा, अगर आप रूट प्रकार (यहां: NumberSet) पैकेज निजी बनाते हैं। या तो उस प्रकार का पर्दाफाश करें या इसके बजाय सार्वजनिक इंटरफ़ेस का उपयोग करें। वास्तविक कार्यान्वयन प्रकारों को छुपाया जाना चाहिए (और कर सकते हैं)।

public abstract class NumberSet { 

    // Constructor is package private, so no new classes can be derived from 
    // this guy outside of its package. 
    NumberSet() { 
    } 
} 

public class Factories { 

    public NumberSet range(int start, int length) { 
     return new RangeNumberSet(start, length); 
    } 

    // ... 
} 

class RangeNumberSet extends NumberSet { 
    // ... must be defined in the same package as NumberSet 
    // Is "invisible" to client code 
} 

संपादित एक सार्वजनिक एपीआई से एक छिपा/निजी जड़ प्रकार का पर्दाफाश करने के लिए एक गलती है। इस परिदृश्य पर विचार करें:

package example; 

class Bar { 
    public void doSomething() { 
      // ... 
    } 
} 

public class Foo { 

    public Bar newBar() { 
     return new Bar(); 
    } 
} 

और इस एपीआई का उपयोग कर एक ग्राहक आवेदन पर विचार करें। ग्राहक क्या कर सकता है? यह टाइप Bar टाइप करने के लिए एक चर घोषित नहीं कर सकता है क्योंकि उस प्रकार पैकेज example के बाहर किसी भी वर्ग के लिए अदृश्य है। Bar उदाहरण पर यह public विधियों को भी कॉल नहीं कर सकता है, क्योंकि यह पता नहीं है कि ऐसी सार्वजनिक विधि मौजूद है (यह वर्ग नहीं देख सकती है, इसे किसी भी सदस्य को उजागर करने दें)।

Object bar = foo.newBar(); 

जो अनिवार्य रूप से बेकार है: तो, सबसे अच्छा एक ग्राहक यहाँ कर सकते हैं की तरह कुछ है। उपरोक्त परिभाषित कोड में, निजी पैकेज के बजाय सार्वजनिक इंटरफेस (या अमूर्त वर्ग) की एक अलग बात होगी। इस मामले में, ग्राहक वास्तव में NumberSet प्रकार का एक चर घोषित कर सकता है। यह अपने स्वयं के उदाहरण नहीं बना सकता है या सबक्लास प्राप्त नहीं कर सकता है, क्योंकि कन्स्ट्रक्टर इससे छिपा हुआ है, लेकिन यह परिभाषित सार्वजनिक एपीआई तक पहुंच सकता है।

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

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

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

public abstract class Specification { 

    Specification() { 
     // Package private, thus not accessible to the client 
     // No subclassing possible 
    } 

    Stuff getInternalValue1() { 
     // Package private, thus not accessible to the client 
     // Client cannot call this 
    } 
} 

उपरोक्त वर्ग क्लाइंट कोड के संबंध में "खाली" है; यह सामान को छोड़कर एक प्रयोग योग्य एपीआई प्रदान नहीं करता है, जो Object पहले से ही ऑफर करता है। इसे रखने का प्रमुख लाभ: ग्राहक इस प्रकार के चर घोषित कर सकता है, और संकलक चेक टाइप करने में सक्षम है। आपका ढांचा, हालांकि, एकमात्र जगह बनी हुई है, जहां इस प्रकार के ठोस उदाहरण बनाए जा सकते हैं, और इस प्रकार, आपके ढांचे के सभी मूल्यों पर पूर्ण नियंत्रण होता है।

+0

यह क्यों काम नहीं करेगा, अगर हम रूट प्रकार पैकेज-निजी बनाते हैं? –

+0

हां, लेकिन यह इस प्रकार को देखने के लिए अवांछित ग्राहक है। हमें कारखाने के तरीकों के माध्यम से विनिर्देश की आवश्यकता है और अनुसूची वस्तुओं के लिए कारखाने के तरीकों के माध्यम से इसे पारित करना है। –

0

मैं यह करने के लिए दो तरीके के बारे में सोच सकते हैं, लेकिन न तो वास्तव में काम करते हैं:

  1. निजी पैकेज के रूप में घोषित NumberSet, लेकिन सार्वजनिक एपीआई तरीकों कि वर्तमान के बजाय Object उपयोग करने के लिए NumberSet प्रकार का उपयोग बदलने के लिए, और कार्यान्वयन के अनुसार आवश्यकतानुसार Object से NumberSet पर तर्क डाले गए हैं। यह कॉलर्स पर हमेशा सही प्रकार की वस्तु को पारित करता है, और ClassCastException एस के लिए प्रवण होगा।

  2. कोई तरीकों के साथ एक सार्वजनिक NumberSet इंटरफ़ेस को लागू और निजी InternalNumberSet कि NumberSet लागू करता है और परिभाषित करता है आंतरिक रूप से आवश्यक तरीकों पैकेज। प्रकार कास्टिंग का प्रयोग एक बार फिर तर्कों को NumberSet से InternalNumberSet पर डालने के लिए किया जाता है। यह पिछला दृष्टिकोण बेहतर है, लेकिन अगर कोई ऐसी कक्षा बनाता है जो NumberSet लागू करता है तो InternalNumberSet को लागू किए बिना, परिणाम ClassCastException एस एक बार फिर होगा।

मुझे व्यक्तिगत रूप से लगता है कि आपको केवल NumberSet इंटरफ़ेस को सार्वजनिक रूप से घोषित करना चाहिए। इंटरफ़ेस में गेटर्स को उजागर करने में कोई वास्तविक नुकसान नहीं है, और यदि आप अपने कार्यान्वयन कक्षाओं को अपरिवर्तनीय के रूप में चाहते हैं, तो उन्हें अंतिम रूप में घोषित करें और सेटर्स का पर्दाफाश न करें।

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