क्या यह हमेशा जावा में "इंटरफ़ेस के लिए प्रोग्राम" करने के लिए समझ में आता है?


9

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

Map<X> map = new TreeMap<X>();

मेरे कार्यक्रम में, मैं फ़ंक्शन को कॉल कर रहा हूं map.pollFirstEntry(), जिसे Mapइंटरफ़ेस में घोषित नहीं किया गया है (और कुछ जोड़े जो Mapइंटरफ़ेस में भी मौजूद हैं)। मैं TreeMap<X>हर जगह इस पद्धति से कॉल करके ऐसा करने में कामयाब रहा हूं जैसे:

someEntry = ((TreeMap<X>) map).pollFirstEntry();

मैं शुरुआती दिशा-निर्देशों के लाभों को बड़े कार्यक्रमों के लिए ऊपर वर्णित के रूप में समझता हूं, हालांकि एक बहुत छोटे कार्यक्रम के लिए जहां यह ऑब्जेक्ट अन्य तरीकों से पारित नहीं होगा, मुझे लगता है कि यह अनावश्यक है। फिर भी, मैं इस नमूना कोड को एक नौकरी आवेदन के हिस्से के रूप में लिख रहा हूं, और मैं नहीं चाहता कि मेरा कोड बुरी तरह से दिखाई दे और न ही बरबाद हो। सबसे सुरुचिपूर्ण समाधान क्या होगा?

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


1
जब आपको सुविधाओं की आवश्यकता हो, तो एक TreeMap का उपयोग करें। यह शायद एक विशिष्ट कारण के लिए एक डिजाइन विकल्प था इसलिए इसे कार्यान्वयन का हिस्सा भी होना चाहिए।

@JordanReiter क्या मुझे एक ही सामग्री के साथ एक नया प्रश्न जोड़ना चाहिए, या एक आंतरिक क्रॉस-पोस्टिंग तंत्र है?
jimijazz


2
"मुझे लगता है कि बड़े कार्यक्रमों के लिए ऊपर वर्णित प्रारंभिक दिशानिर्देशों के फायदे" सभी जगह कास्ट करने के लिए फायदेमंद नहीं है, चाहे कोई भी कार्यक्रम का आकार क्यों न हो
बेन आरोनसन

जवाबों:


23

"एक इंटरफेस के लिए प्रोग्रामिंग" का मतलब यह नहीं है "संभव सबसे सार संस्करण का उपयोग करें"। उस मामले में हर कोई बस का उपयोग करेगा Object

इसका मतलब यह है कि आपको कार्यक्षमता खोए बिना सबसे कम संभव अमूर्तता के खिलाफ अपने कार्यक्रम को परिभाषित करना चाहिए । यदि आपको आवश्यकता है TreeMapतो आपको एक अनुबंध का उपयोग करके परिभाषित करना होगा TreeMap


2
ट्रीपाइप एक इंटरफ़ेस नहीं है, इसका कार्यान्वयन वर्ग है। यह इंटरफेस मैप, SortedMap और NavigableMap को लागू करता है। वर्णित विधि NavigableMap इंटरफ़ेस का हिस्सा है । ट्रीपाउप का उपयोग कार्यान्वयन को एक समवर्तीSkipListMap (उदाहरण के लिए) पर स्विच करने से रोकता है, जो कार्यान्वयन के बजाय इंटरफ़ेस को कोड करने का संपूर्ण बिंदु है।

3
@ मिचेल्ट: मैंने इस विशिष्ट परिदृश्य में सटीक अमूर्तता को नहीं देखा, इसलिए मैंने TreeMapएक उदाहरण के रूप में उपयोग किया । "एक इंटरफ़ेस के लिए कार्यक्रम" को शाब्दिक रूप से एक इंटरफ़ेस या एक सार वर्ग के रूप में नहीं लिया जाना चाहिए - एक कार्यान्वयन को एक इंटरफ़ेस भी माना जा सकता है।
जीरो वेनवेल

1
भले ही कार्यान्वयन वर्ग का सार्वजनिक इंटरफ़ेस / विधियाँ तकनीकी रूप से एक 'इंटरफ़ेस' है, यह एलएसपी के पीछे की अवधारणा को तोड़ता है और एक अलग उपवर्ग के प्रतिस्थापन को रोकता है, यही कारण है कि आप public interface'कार्यान्वयन के सार्वजनिक तरीकों' के बजाय कार्यक्रम करना चाहते हैं ।

@JeroenVannevel मैं सहमत हूं कि इंटरफ़ेस के लिए प्रोग्रामिंग तब की जा सकती है जब इंटरफ़ेस वास्तव में एक वर्ग द्वारा प्रस्तुत किया जाता है। हालांकि, मैं का उपयोग कर लाभ नहीं दिख रहा है TreeMapखत्म हो गया होता SortedMapयाNavigableMap
toniedzwiedz

16

यदि आप अभी भी एक इंटरफ़ेस का उपयोग करना चाहते हैं, जिसका आप उपयोग कर सकते हैं

NavigableMap <X, Y> map = new TreeMap<X, Y>();

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


3

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

उस स्थिति पर विचार करें जहां आपके कोड के मूल संस्करण ने एक का उपयोग किया HashMapऔर उसे उजागर किया।

private HashMap foo = new HashMap();
public HashMap getFoo() { return foo; }  // This is bad, don't do this.

इसका मतलब है कि कोई भी परिवर्तन getFoo()एक ब्रेकिंग एपीआई परिवर्तन है और इससे लोग दुखी हो जाएंगे। यदि आप जो गारंटी दे रहे हैं foo, वह एक नक्शा है, तो आपको इसके बजाय वापस लौटना चाहिए।

private Map foo = new HashMap();
public Map getFoo() { return foo; }

यह आपको यह बदलने की सुविधा देता है कि चीजें आपके कोड के अंदर कैसे काम करती हैं। आपको एहसास होता है कि fooवास्तव में एक मैप होना चाहिए जो किसी विशेष क्रम में चीजों को वापस करता है।

private NavigableMap foo = new TreeMap();
public Map getFoo() { return foo; }
private void doBar() { ... foo.lastEntry(); ... }

और यह बाकी कोड के लिए कुछ भी नहीं तोड़ता है।

आप बाद में उस अनुबंध को मजबूत कर सकते हैं जो कक्षा को बिना कुछ भी तोड़े देता है।

private NavigableMap foo = new TreeMap();
public NavigableMap getFoo() { return foo; }
private void doBar() { ... foo.lastEntry(); ... }

यह लिस्कोव प्रतिस्थापन सिद्धांत में तल्लीन करता है

सबसिडीबिलिटी ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में एक सिद्धांत है। इसमें कहा गया है कि, एक कंप्यूटर प्रोग्राम में, यदि S, T का उपप्रकार है, तो टाइप T की वस्तुओं को S की वस्तुओं से बदला जा सकता है (अर्थात, टाइप S की वस्तुएं टाइप T की वस्तुओं को स्थानापन्न कर सकती हैं) बिना किसी वांछित को बदले। उस कार्यक्रम के गुण (शुद्धता, कार्य प्रदर्शन, आदि)।

चूंकि NavigableMap मानचित्र का एक उपप्रकार है, इसलिए इस प्रतिस्थापन को कार्यक्रम में बदलाव किए बिना किया जा सकता है।

कार्यान्वयन प्रकार उजागर यह मुश्किल कैसे इस कार्यक्रम आंतरिक रूप से काम करता है जब एक परिवर्तन किया जाने की आवश्यकता को बदलने के लिए बनाता है। यह एक दर्दनाक प्रक्रिया है और कई बार बदसूरत समाधान है कि केवल बाद में सांकेतिक शब्दों में बदलनेवाला के बारे में अधिक दर्द दण्ड की सेवा बनाता है (मैं आप पर पिछले सांकेतिक शब्दों में बदलनेवाला जो एक LinkedHashMap और किसी कारण के लिए एक ट्री-मैप के बीच डेटा फेरबदल रखा देख रहा हूँ -, जब भी मैं विश्वास मुझे अपना नाम svn blame I चिंता में देखें)।

आप चाहते हैं अभी भी कार्यान्वयन प्रकार लीक से बचना चाहते हैं। उदाहरण के लिए, आप या जैसे बजाय कुछ प्रदर्शन विशेषताओं के कारण ConcurrentSkipListMap लागू करने के लिए इच्छा हो सकती है आप java.util.concurrent.ConcurrentSkipListMapके बजाय java.util.TreeMapमें आयात बयान या जो कुछ भी।


1

अन्य उत्तरों से सहमत होना चाहिए कि आपको सबसे सामान्य वर्ग (या इंटरफ़ेस) का उपयोग करना चाहिए, जिसके लिए आपको वास्तव में किसी चीज़ की आवश्यकता है, इस मामले में TreeMap (या किसी ने नवजीवन का सुझाव दिया)। लेकिन मैं यह जोड़ना चाहूंगा कि यह निश्चित रूप से हर जगह कास्टिंग से बेहतर है, जो किसी भी मामले में बहुत बड़ी गंध होगी। Https://stackoverflow.com/questions/4167304/why-should-casting-be-avoided कुछ कारणों से देखें ।


1

यह आपके इरादे को संप्रेषित करने के बारे में है कि वस्तु का उपयोग कैसे किया जाना चाहिए। उदाहरण के लिए, यदि आपकी विधि किसी Mapवस्तु के पूर्वानुमान के क्रम के साथ उम्मीद करती है :

private Map<String, String> processOrderedMap(LinkedHashMap<String, String> input) {
    // ...
}

फिर, यदि आपको उपरोक्त विधि के कॉलर्स को यह बताने की आवश्यकता है कि यह एक Mapवस्तु की भविष्यवाणी के क्रम के साथ भी लौटाता है , क्योंकि किसी कारण से ऐसी उम्मीद है:

private LinkedHashMap<String,String> processOrderedMap(LinkedHashMap<String,String> input) {
    // ...
}

बेशक, कॉलर अभी भी रिटर्न ऑब्जेक्ट Mapको इस तरह से मान सकते हैं, लेकिन यह आपके तरीके के दायरे से परे है:

private Map<String, String> output = processOrderedMap(input);

एक कदम पीछे हटते हुए

एक इंटरफ़ेस के लिए कोडिंग की सामान्य सलाह (आम तौर पर) लागू होती है, क्योंकि आमतौर पर यह इंटरफ़ेस होता है जो गारंटी प्रदान करता है कि ऑब्जेक्ट को प्रदर्शन करने में सक्षम होना चाहिए, अनुबंध । कई शुरुआती के साथ शुरू होता है HashMap<K, V> map = new HashMap<>()और उन्हें सलाह दी जाती है कि वे इसे घोषित करें Map, क्योंकि जो HashMapकुछ भी करने की अपेक्षा नहीं है उससे अधिक की पेशकश करता Mapहै। इसके बाद, वे तब समझ पाएंगे (उम्मीद है) कि उनके तरीकों को एक के Mapबजाय क्यों लेना चाहिए HashMap, और इससे उन्हें ओओपी में विरासत की सुविधा का एहसास होता है।

इस विषय से संबंधित सभी के पसंदीदा सिद्धांत के विकिपीडिया प्रविष्टि से सिर्फ एक पंक्ति को उद्धृत करने के लिए :

यह सिर्फ़ वाक्यात्मक संबंध के बजाय एक शब्दार्थ है क्योंकि यह एक पदानुक्रम में प्रकारों की शब्दार्थक अंतरसंयोजकता की गारंटी देने का इरादा रखता है ...

दूसरे शब्दों में, एक Mapघोषणा का उपयोग इसलिए नहीं किया जाता है क्योंकि यह 'वाक्य रचना' करता है, बल्कि ऑब्जेक्ट पर इनवोकेशन केवल यह ध्यान रखना चाहिए कि यह एक प्रकार का है Map

क्लीनर कोड

मुझे लगता है कि यह मुझे कई बार क्लीनर कोड लिखने की अनुमति देता है, खासकर जब यह इकाई परीक्षण की बात आती है। HashMapएक ही रीड-ओनली टेस्ट प्रविष्टि के साथ बनाना एक लाइन (डबल-ब्रेस इनिशियलाइज़ेशन के उपयोग को छोड़कर) से अधिक लेता है, जब मैं आसानी से उस के साथ प्रतिस्थापित कर सकता हूं Collections.singletonMap()

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