2010-08-07 9 views
6

मैं खुद को निम्नलिखित समस्या से नियमित रूप से सामना करता हूं। मैं मार्कर इंटरफ़ेस के कुछ प्रकार है (सादगी के लिए के java.io.Serializable का उपयोग करते हैं) और कई रैपर (एडाप्टर, डेकोरेटर, प्रॉक्सी, ...)। लेकिन जब आप किसी अन्य उदाहरण में एक धारावाहिक उदाहरण को लपेटते हैं (जो धारावाहिक नहीं है) तो आप कार्यक्षमता खो देते हैं। Java.util.RandomAccess के साथ एक ही समस्या होती है जिसे सूची कार्यान्वयन द्वारा कार्यान्वित किया जा सकता है। क्या इसे संभालने के लिए एक अच्छा ओओपी तरीका है?क्या रचना और मार्कर इंटरफेस के लिए कोई कामकाज है?

+0

"... जब आप लपेट ... आप कार्यक्षमता खो" - तुम्हारा मतलब लिपटे serializable उदाहरण इस तरह नहीं रह गया है? – anirvan

+0

हां, क्योंकि बाहरी उदाहरण Serializable लागू नहीं करता है। – whiskeysierra

+1

अच्छा सवाल। मेरे विचार में यह एक बहुत ही सामान्य समस्या है, और हमें इसे बेहतर समझने की आवश्यकता है। –

उत्तर

7

यहाँ अमरूद मेलिंग सूची पर हाल ही में चर्चा है: इंटरफेस सिर्फ एक मार्कर नहीं है, लेकिन वास्तव में कुछ कार्यक्षमता है, तो आप एक विधि खोलने में जोड़ सकते हैं।

http://groups.google.com/group/guava-discuss/browse_thread/thread/2d422600e7f87367/1e6c6a7b41c87aac

यह का सार यह है: मार्कर इंटरफेस का उपयोग न करें जब आप उम्मीद कर अपने वस्तुओं लिपटे किया जाना है। एक ArrayList

उदाहरण के लिए, - (कैसे आप जानते हैं कि आपके वस्तु एक ग्राहक द्वारा लपेटा जा करने के लिए नहीं जा रहा है ठीक है, यह बहुत सामान्य है?)। यह स्पष्ट रूप से RandomAccess लागू करता है। फिर आप List ऑब्जेक्ट्स के लिए एक रैपर बनाने का निर्णय लेते हैं। ऊप्स! अब जब आप लपेटते हैं, तो आपको लपेटा हुआ ऑब्जेक्ट देखना होगा, और यदि यह RandomAccess है, तो आपके द्वारा बनाए गए रैपर को भी RandomAccess लागू करना चाहिए!

यह "ठीक" काम करता है ... यदि आपके पास केवल एक मार्कर इंटरफ़ेस है! लेकिन क्या होगा यदि लपेटा हुआ वस्तु Serializable हो सकता है? अगर ऐसा होता है, तो कहें, "अपरिवर्तनीय" (मान लीजिए कि आपके पास यह इंगित करने के लिए एक प्रकार है)? या तुल्यकालिक? (एक ही धारणा के साथ)।

जैसा कि मैंने मेलिंग सूची के जवाब में भी ध्यान दिया है, यह डिज़ाइन की कमी भी पुराने पुराने java.io पैकेज में स्वयं को प्रकट करती है। मान लें कि आपके पास InputStream स्वीकार करने का तरीका है। क्या आप इससे सीधे पढ़ेंगे? क्या होगा यदि यह एक महंगा धारा है, और कोई भी आपके लिए BufferedInputStream में इसे लपेटने की परवाह नहीं करता है? ओह, यह आसान है! आप बस stream instanceof BufferedInputStream देखें, और यदि नहीं, तो आप इसे स्वयं लपेटें! लेकिन नहीं। धारा में श्रृंखला के नीचे कहीं भी बफरिंग हो सकती है, लेकिन आप इसका एक रैपर प्राप्त कर सकते हैं, जो BufferedInputStream का उदाहरण नहीं है। इस प्रकार, "इस स्ट्रीम को बफर किया गया" जानकारी खो गई है (और आपको इसे फिर से बफर करने के लिए स्मृति को निराशाजनक रूप से बर्बाद करना होगा)।

यदि आप चीजें ठीक से करना चाहते हैं, तो ऑब्जेक्ट्स के रूप में क्षमताओं को मॉडल करें। पर विचार करें:

interface YourType { 
    Set<Capability> myCapabilities(); 
} 

enum Capability { 
    SERIALIAZABLE, 
    SYNCHRONOUS, 
    IMMUTABLE, 
    BUFFERED //whatever - hey, this is just an example, 
      //don't throw everything in of course! 
} 

संपादित करें: ऐसा लगता है कि मैं सुविधा के लिए सिर्फ एक enum का उपयोग करें। एक इंटरफ़ेस Capability और इसे कार्यान्वित करने वाले ऑब्जेक्ट्स का एक ओपन-एंड सेट (शायद एकाधिक enums) द्वारा किया जा सकता है।

तो जब आप इनमें से एक वस्तु लपेट, तो आप एक सेट क्षमताओं का मिलता है, और आप आसानी से बनाए रखने के लिए, दूर करने के लिए जो, जोड़ने के लिए जो जो क्षमताओं तय कर सकते हैं।

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

<T extends List<Integer> & RandomAccess & Serializable> void method(T list) { ... } 

लेकिन दृष्टिकोण मैं का वर्णन में, तुम सब कर सकते हैं:

void method(YourType object) { 
    Preconditions.checkArgument(object.getCapabilities().contains(SERIALIZABLE)); 
    Preconditions.checkArgument(object.getCapabilities().contains(RANDOM_ACCESS)); 
    ... 
} 

मैं वास्तव में चाहते हैं या तो तुलना में एक अधिक संतोषजनक दृष्टिकोण थे, लेकिन दृष्टिकोण से, यह ऐसा करने योग्य नहीं लगता है (कम से कम, बिना संयोजन के विस्फोट के कारण)।

संपादित करें: एक और कमी है कि, क्षमता प्रति एक स्पष्ट प्रकार के बिना, हम तरीकों कि व्यक्त क्या यह क्षमता प्रदान करता है डाल करने के लिए प्राकृतिक स्थान पर नहीं है। यह इस चर्चा में भी महत्वपूर्ण हैं क्योंकि हम के बारे में मार्कर इंटरफेस है, यानी क्षमताओं है कि अतिरिक्त तरीकों के माध्यम से व्यक्त नहीं कर रहे हैं बात नहीं है, लेकिन मैं इसे पूर्णता के लिए उल्लेख किया है।

पुनश्च: वैसे, अगर आप अमरूद के संग्रह कोड के माध्यम से स्किम, तुम सच में दर्द है कि इस समस्या का कारण है महसूस कर सकते हैं। हां, कुछ अच्छे लोग इसे अच्छे abstractions के पीछे छिपाने की कोशिश कर रहे हैं, लेकिन अंतर्निहित मुद्दा दर्दनाक है फिर भी।

+0

मैं एक अच्छा समाधान की उम्मीद कर रहा था लेकिन मुझे पहले से ही उम्मीद है कि "नहीं, लेकिन ..." - उत्तर। मुझे तुम्हारा सबसे ज्यादा पसंद है। – whiskeysierra

1

RandomAccess की पसंद के लिए वहाँ ज्यादा आप कर सकते हैं नहीं है। आप निश्चित रूप से instanceof कर सकते हैं और प्रासंगिक वर्ग का उदाहरण बना सकते हैं। वर्गों की संख्या मार्कर के साथ तेजी से बढ़ता है (आप java.lang.reflect.Proxy इस्तेमाल कर सकते हैं, हालांकि) और अपनी रचना विधि कभी सभी मार्करों के बारे में पता करने की जरूरत है।

Serializable इतना बुरा नहीं है। यदि लक्ष्य वर्ग Serializable है और न अविवेक वर्ग Serializable लागू करता है तो पूरे serialisable हो सकता है अगर ऐसा नहीं है।

+0

ठीक है, 'Serializable' एक * बुरा * उदाहरण था। क्या आप जानते हैं कि अन्य भाषाओं/अवधारणाओं (जावा से अलग) कैसे संभालती हैं? – whiskeysierra

1

कुछ विकल्प हैं, हालांकि कोई भी बहुत अच्छा

  1. आवरण इंटरफ़ेस को लागू है, अगर यह संकलन समय पर जाना जाता है, तो लिपटे वस्तु भी इंटरफ़ेस लागू करता है सुनिश्चित कर रहे हैं। रैपर बनाने के लिए एक फैक्ट्री विधि का उपयोग किया जा सकता है यदि यह रनटाइम तक ज्ञात नहीं है यदि लपेटा हुआ ऑब्जेक्ट इंटरफेस को कार्यान्वित करेगा। इसका मतलब है कि आपके पास कार्यान्वित इंटरफेस के संभावित संयोजनों के लिए अलग रैपर वर्ग हैं। (एक इंटरफ़ेस के साथ, आपको 2 रैपर की आवश्यकता होती है, एक के साथ एक और बिना। 2 इंटरफेस, 4 रैपर और इतने पर।)

  2. रैपर से लिपटे ऑब्जेक्ट्स का पर्दाफाश करें, ताकि ग्राहक श्रृंखला चल सकें और प्रत्येक का परीक्षण कर सकें instanceof का उपयोग कर इंटरफ़ेस के लिए श्रृंखला में ऑब्जेक्ट करें। यह encapsulation तोड़ता है।

  3. इंटरफेस, दोनों आवरण और लिपटे वस्तु द्वारा कार्यान्वित पुनः प्राप्त करने के लिए एक समर्पित विधि है। जैसे asSomeInterface()। रैपर लपेटा हुआ ऑब्जेक्ट में प्रतिनिधि करता है, या encapsulation को संरक्षित करने के लिए लपेटा हुआ ऑब्जेक्ट के चारों ओर एक प्रॉक्सी बनाता है।

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

माइक्रोसॉफ्ट उनके घटक ऑब्जेक्ट मॉडल (COM) में aggregation (Wikipedia) पके हुए। ऐसा लगता है कि बहुमत से अप्रयुक्त है, फिर भी COM ऑब्जेक्ट कार्यान्वयनकर्ताओं के लिए काफी जटिलता है, क्योंकि नियम हैं कि प्रत्येक ऑब्जेक्ट का पालन करना चाहिए। लपेटा हुआ ऑब्जेक्ट्स लपेटकर ऑब्जेक्ट्स को एन्सेप्लेटेड होते हैं, वे लपेटने वाले ऑब्जेक्ट्स के बारे में जानते हैं, रैपर को पॉइंटर बनाए रखने के लिए, जिसका उपयोग खुला सार्वजनिक इंटरफेस के लिए QueryInterface (loosely instanceof) को लागू करते समय किया जाता है - लपेटा हुआ ऑब्जेक्ट रैपर पर लागू इंटरफ़ेस देता है इसके अपने कार्यान्वयन के बजाए।

मैंने इसे साफ, आसान समझने/कार्यान्वित करने और सही तरीके से encapsulated समाधान नहीं देखा है। COM एकत्रीकरण कार्य करता है और पूर्ण encapsulation प्रदान करता है, लेकिन यह एक लागत है जिसे आप लागू करने वाली प्रत्येक वस्तु के लिए भुगतान करते हैं, भले ही इसका उपयोग कुल में कभी नहीं किया जाता है।

5

इंटरफेस में आपकी रुचि है सभी मार्कर इंटरफेस हैं, तो आप अपने सभी आवरण कक्षाएं एक अंतरफलक

public interface Wrapper { 
    boolean isWrapperFor(Class<?> iface); 
} 

जिसका कार्यान्वयन इस प्रकार दिखाई देगा लागू हो सकता है:

public boolean isWrapperFor(Class<?> cls) { 
    if (wrappedObj instanceof Wrapper) { 
     return ((Wrapper)wrappedObj).isWrapperFor(cls); 
    } 
    return cls.isInstance(wrappedObj); 
} 

यह वह जगह है यह java.sql.Wrapper में कैसे किया जाता है। मेरा उत्तर छूता है इस पर नहीं बल्कि मौलिक मुद्दा, -

<T> T unwrap(java.lang.Class<T> cls) 
+1

यह काम करता है, निम्नलिखित प्राकृतिक गलती पर विचार करें: एक मार्कर इंटरफ़ेस एक्स को देखते हुए, और एक (लिपटे) वस्तु ओ, एक ग्राहक के लिए झुकाव पारंपरिक लिखने के लिए 'ओ X' है, जो चुपचाप झूठी वापसी होगी instanceof है, बजाय किसी भी तरह हथियाने त्रुटि के लिए प्रोग्रामर का ध्यान। मुझे लगता है कि यह ** ** ** दोनों मार्कर इंटरफेस रखने के लिए खतरनाक/प्रतिद्वंद्वी है, और परीक्षण के उदाहरण के अर्थहीन बनाते हैं। (cont'd) –

+1

मेरे उत्तर में, मैं उदाहरण के साथ एक विधि कॉल के साथ प्रतिस्थापित करता हूं, वैसे ही आपके लिए, मार्कर इंटरफेस के साथ संयोजन के बावजूद। इन्हें किसी भी मूल्य के साथ प्रतिस्थापित किया जा सकता है, जैसे MyEnum.CAPABILITY_ONE, ताकि उपयोगकर्ता संभवतया गलत समझ न सके और 'x exampleof MyEnum.CAPABILITY_ONE' करें।/उम्मीद है कि नोट्स की तुलना करना उपयोगी है :) –

+0

नोट्स की तुलना करना हमेशा उपयोगी होता है :) बिल्कुल मुझे आपका जवाब बहुत पसंद आया (आपको +1 दिया गया); लेकिन इसमें कुछ कमियां हैं - स्पष्ट बात यह है कि आपको अपनी सभी क्षमताओं को पहले से परिभाषित करने की आवश्यकता होगी (इसलिए किसी के लिए क्षमता एनम को संशोधित किए बिना किसी प्रकार की सूची बनाने का कोई तरीका नहीं है); – mkadunc

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