कई इंटरफ़ेस को संयोजित करने के लिए खाली इंटरफ़ेस


20

मान लीजिए कि आपके पास दो इंटरफेस हैं:

interface Readable {
    public void read();
}

interface Writable {
    public void write();
}

कुछ मामलों में लागू करने वाली वस्तुएँ केवल इनमें से किसी एक का समर्थन कर सकती हैं, लेकिन बहुत सारे मामलों में कार्यान्वयन दोनों इंटरफेस का समर्थन करेंगे। इंटरफेस का उपयोग करने वाले लोगों को कुछ ऐसा करना होगा:

// can't write to it without explicit casting
Readable myObject = new MyObject();

// can't read from it without explicit casting
Writable myObject = new MyObject();

// tight coupling to actual implementation
MyObject myObject = new MyObject();

इन विकल्पों में से कोई भी बहुत सुविधाजनक नहीं है, और भी अधिक जब विचार करें कि आप इसे विधि पैरामीटर के रूप में चाहते हैं।

एक समाधान एक रैपिंग इंटरफ़ेस घोषित करना होगा:

interface TheWholeShabam extends Readable, Writable {}

सभी कार्यान्वयन का समर्थन करने वाले दोनों पठनीय और लिखने योग्य: लेकिन यह एक विशिष्ट समस्या है है TheWholeShabam लागू करने के लिए अगर वे इंटरफ़ेस का उपयोग कर लोगों के साथ संगत होना चाहता हूँ। भले ही यह दोनों इंटरफेस की गारंटीकृत उपस्थिति के अलावा कुछ नहीं प्रदान करता है।

क्या इस समस्या का कोई साफ समाधान है या मुझे रैपर इंटरफ़ेस के लिए जाना चाहिए?

अपडेट करें

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

UPDATE2

(उत्तर के रूप में निकाला गया तो इस पर टिप्पणी करना आसान है)

Update3

कृपया सावधान रहें कि इसके लिए प्राथमिक usecase स्ट्रीम नहीं है (हालाँकि उन्हें भी समर्थित होना चाहिए)। धाराएँ इनपुट और आउटपुट के बीच बहुत विशिष्ट अंतर बनाती हैं और जिम्मेदारियों का स्पष्ट पृथक्करण होता है। इसके बजाय, एक बाइटबफ़र जैसी चीज़ के बारे में सोचें, जहाँ आपको एक ऐसी वस्तु की आवश्यकता हो जिसे आप लिख सकते हैं और उससे पढ़ सकते हैं, एक ऐसी वस्तु जिसके पास एक बहुत ही विशिष्ट स्थिति है। ये वस्तुएं मौजूद हैं क्योंकि वे एसिंक्रोनस I / O जैसी कुछ चीजों के लिए बहुत उपयोगी हैं, एन्कोडिंग, ...

UPDATE4

मेरी कोशिश की गई पहली चीजों में से एक नीचे दिए गए सुझाव के समान थी (स्वीकार किए गए उत्तर की जांच करें) लेकिन यह बहुत नाजुक साबित हुई।

मान लीजिए कि आपके पास एक वर्ग है जिसे टाइप वापस करना है:

public <RW extends Readable & Writable> RW getItAll();

यदि आप इस विधि को कॉल करते हैं, तो जेनेरिक आरडब्ल्यू को ऑब्जेक्ट प्राप्त करने वाले चर द्वारा निर्धारित किया जाता है, इसलिए आपको इस संस्करण का वर्णन करने के लिए एक तरीका चाहिए।

MyObject myObject = someInstance.getItAll();

यह काम करेगा लेकिन एक बार फिर इसे लागू करने के लिए बाध्य करता है और वास्तव में रनटाइम पर क्लासकैस्टेसेप्शन फेंक सकता है (जो लौटा है उसके आधार पर)।

इसके अतिरिक्त यदि आप RW प्रकार का एक वर्ग चर चाहते हैं, तो आपको कक्षा स्तर पर जेनेरिक को परिभाषित करने की आवश्यकता है।


5
वाक्यांश "पूरे
शेबंग

यह एक अच्छा सवाल है, लेकिन मुझे लगता है कि पठनीय और 'लिखने योग्य' का उपयोग करना आपके उदाहरण के इंटरफेस के रूप में पानी को कुछ हद तक
मैला

@Basueta नामकरण को सरल बनाया गया है, लेकिन पठनीय और योग्य वास्तव में मेरे usecase को अच्छी तरह से व्यक्त करता है। कुछ मामलों में आप केवल पढ़ना चाहते हैं, कुछ मामलों में केवल लिखते हैं और आश्चर्यजनक रूप से पढ़ने और लिखने वाले मामलों में।
nablex

मैं उस समय के बारे में नहीं सोच सकता जब मुझे एक एकल धारा की आवश्यकता होती है जो पठनीय और लेखन योग्य हो, और अन्य लोगों के उत्तरों / टिप्पणियों को देखते हुए मुझे नहीं लगता कि मैं केवल एक ही हूं। मैं सिर्फ यह कह रहा हूं कि यह कम विवादास्पद जोड़ी को चुनने के लिए अधिक उपयोगी हो सकता है ...
vaughandroid

@ बटुआ java.nio * पैकेज के साथ क्या करना है? यदि आप usecase को स्ट्रीम करने के लिए चिपके रहते हैं, तो आप वास्तव में उन जगहों तक ही सीमित हैं, जहाँ आप * ByteArray * स्ट्रीम का उपयोग करेंगे।
nablex

जवाबों:


19

हां, आप अपने विधि पैरामीटर को एक अंडरस्क्राइब्ड प्रकार के रूप में घोषित कर सकते हैं जो दोनों का विस्तार करता है Readableऔर Writable:

public <RW extends Readable & Writable> void process(RW thing);

विधि घोषणा भयानक लग रही है, लेकिन इसका उपयोग एकीकृत इंटरफ़ेस के बारे में जानना आसान है।


2
मैं यहां कोन्रैड्स के दूसरे दृष्टिकोण को बहुत पसंद करता हूं: process(Readable readThing, Writable writeThing)और यदि आपको इसका उपयोग करना चाहिए process(foo, foo)
जोआचिम सौर

1
सही वाक्यविन्यास नहीं है <RW extends Readable&Writable>?
आया

1
@JoachimSauer: आप एक ऐसे दृष्टिकोण को क्यों पसंद करेंगे जो आसानी से एक से अधिक बदसूरत हो? अगर मैं प्रक्रिया (फू, बार) और फू और बार कहता हूं, तो विधि विफल हो सकती है।
माइकल शॉ

@ मिचेलशॉ: मैं जो कह रहा हूं वह यह है कि जब वे अलग-अलग वस्तुएं हों तो यह विफल नहीं होनी चाहिए । क्यों करना चाहिए? यदि ऐसा होता है, तो मेरा तर्क है कि processकई अलग-अलग काम करता है और एकल-जिम्मेदारी सिद्धांत का उल्लंघन करता है।
जोआचिम सॉर

@JoachimSauer: यह असफल क्यों नहीं होगा? (i = 0; j <100; i ++) के लिए एक लूप जितना उपयोगी नहीं है (i = 0; i <100; i ++)। लूप के लिए दोनों एक ही चर में पढ़ते और लिखते हैं, और ऐसा करने के लिए यह SRP का उल्लंघन नहीं करता है।
माइकल शॉ

12

यदि कोई ऐसी जगह है जहाँ आपको myObjectदोनों की आवश्यकता है Readableऔर Writableआप कर सकते हैं:

  • उस जगह को रिफलेक्टर? पढ़ना और लिखना दो अलग-अलग चीजें हैं। यदि कोई विधि दोनों करती है, तो शायद वह एकल जिम्मेदारी सिद्धांत का पालन नहीं करती है।

  • दर्रा myObjectदो बार, एक के रूप में Readableऔर एक के रूप में Writable(दो तर्क)। विधि क्या परवाह करती है कि यह एक ही वस्तु है या नहीं?


1
यह तब काम कर सकता है जब आप इसे एक तर्क के रूप में उपयोग करते हैं और वे अलग-अलग चिंताएँ हैं। हालाँकि कभी-कभी आप वास्तव में एक ऐसी वस्तु चाहते हैं जो एक ही समय में पठनीय और लिखने योग्य हो (उसी कारण से आप उदाहरण के लिए ByteArrayOutputStream का उपयोग करना चाहते हैं)
nablex

कैसे? आउटपुट स्ट्रीम लिखते हैं, जैसा कि नाम इंगित करता है - यह इनपुट स्ट्रीम है जो पढ़ सकता है। सी # में समान - एक StreamWriterबनाम StreamReader(और कई अन्य) हैं
कोनराड मोरावस्की

आप बाइट्स (toByteArray ()) को पाने के लिए एक बाइटएयरऑउटपुटस्ट्रीम में लिखते हैं। यह लिखने + पढ़ने के बराबर है। इंटरफेस के पीछे वास्तविक कार्यक्षमता बहुत अधिक है, लेकिन अधिक सामान्य तरीके से। कभी-कभी आप केवल पढ़ना या लिखना चाहेंगे, कभी-कभी आप दोनों को चाहते हैं। एक अन्य उदाहरण बाइटबफ़र है।
nablex

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

2
@ घोषी समस्या यह है कि चिंताएँ हमेशा अलग नहीं होती हैं। कभी-कभी आप एक ऐसी वस्तु चाहते हैं, जो पढ़ और लिख सके, और आप इस बात की गारंटी चाहते हैं कि यह एक ही वस्तु हो (जैसे कि
बाइटएयरऑउटपुटस्ट्रीम

4

वर्तमान में कोई भी उत्तर उस स्थिति को संबोधित नहीं करता है जब आपको पढ़ने योग्य या लिखने योग्य होने की आवश्यकता नहीं है लेकिन दोनों । आपको इस बात की गारंटी है कि A को लिखते समय, आप उस डेटा को A से वापस पढ़ सकते हैं, A से नहीं लिख सकते हैं और B से पढ़ सकते हैं और आशा करते हैं कि वे वास्तव में एक ही वस्तु हैं। Usecases बहुतायत से हैं, उदाहरण के लिए हर जगह आप एक बाइटबफर का उपयोग करेंगे।

फिर भी, मैंने लगभग उस मॉड्यूल को पूरा कर लिया है जिस पर मैं काम कर रहा हूं और वर्तमान में मैंने रैपर इंटरफ़ेस का विकल्प चुना है:

interface Container extends Readable, Writable {}

अब आप कम से कम कर सकते हैं:

Container container = IOUtils.newContainer();
container.write("something".getBytes());
System.out.println(IOUtils.toString(container));

कंटेनर के अपने स्वयं के कार्यान्वयन (वर्तमान में 3) सभी कंटेनर को अलग-अलग इंटरफेस के विपरीत लागू करते हैं, लेकिन क्या किसी को इसे लागू करने में भूल जाना चाहिए, IOUtils एक उपयोगिता विधि प्रदान करता है:

Readable myReadable = ...;
// assuming myReadable is also Writable you can do this:
Container container = IOUtils.toByteContainer(myReadable);

मुझे पता है कि यह इष्टतम समाधान नहीं है, लेकिन यह अभी भी सबसे अच्छा तरीका है क्योंकि कंटेनर अभी भी एक काफी बड़ा उपयोग है।


1
मुझे लगता है कि यह बिल्कुल ठीक है। अन्य उत्तरों में प्रस्तावित कुछ अन्य दृष्टिकोणों से बेहतर है।
टॉम एंडरसन

0

एक सामान्य MyObject उदाहरण को देखते हुए, आपको हमेशा यह जानने की ज़रूरत है कि क्या यह पढ़ने या लिखने का समर्थन करता है। तो आपके पास कोड होगा:

if (myObject instanceof Readable)  {
    Readable  r = (Readable) myObject;
    readThisReadable( r );
}

साधारण मामले में, मुझे नहीं लगता कि आप इस पर सुधार कर सकते हैं। लेकिन अगर पढ़ने के बाद Readable को किसी अन्य फ़ाइल में लिखनाreadThisReadable चाहते हैं , तो यह अजीब हो जाता है।

तो मैं शायद इसके साथ जाऊंगा:

interface TheWholeShabam  {
    public boolean  isReadable();
    public boolean  isWriteable();
    public void     read();
    public void     write();
}

इसे एक पैरामीटर के रूप में लेते हुए readThisReadable, अब readThisWholeShabam, किसी भी वर्ग को संभाल सकता है जो कि TheWholeShabam को लागू करता है, न कि केवल MyObject को। और यह लिख सकता है अगर यह लिखने योग्य है और यह नहीं लिखता है अगर यह नहीं है। (हमें असली "बहुरूपता" मिला है।)

तो कोड का पहला सेट बन जाता है:

TheWholeShabam  myObject = ...;
if (myObject.isReadable()
    readThisWholeShebam( myObject );

और आप यहाँ पढ़ सकते हैं एक पंक्ति को सहेज कर पढ़ सकते हैं।

इसका मतलब यह है कि हमारे पूर्व पठनीय-केवल को लागू करने योग्य है () (वापस झूठे ) और लिखने () (कुछ भी नहीं कर रहा है) को लागू करने के लिए , लेकिन अब यह उन सभी प्रकार के स्थानों पर जा सकता है जो पहले नहीं जा सकते थे और सभी कोड जो TheWholeShabam को संभालते थे वस्तुएं हमारी ओर से किसी और प्रयास के बिना इससे निपटेंगी।

एक अन्य बात: यदि आप एक क्लास में पढ़ने के लिए कॉल () पढ़ नहीं सकते हैं और एक क्लास में लिखने के लिए कॉल नहीं करते हैं () जो किसी चीज़ को ट्रेश किए बिना नहीं लिखता है, तो आप isReadable () और isWriteable को छोड़ सकते हैं () विधियाँ। यह इसे संभालने का सबसे सुरुचिपूर्ण तरीका होगा - यदि यह काम करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.