इंटरफ़ेस डिज़ाइन में जेनरिक का उपयोग कब करें


11

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

वर्तमान में, वे के रूप में परिभाषित कर रहे हैं

आइटम:

public interface Item {

    String getId();

    String getName();
}

ItemStack:

public interface ItemStackFactory {

    ItemStack createItemStack(Item item, int quantity);
}

ItemStackContainer:

public interface ItemStackContainer {

    default void add(ItemStack stack) {
        add(stack, 1);
    }

    void add(ItemStack stack, int quantity);
}

अब, Itemऔर ItemStackFactoryमैं भविष्य में इसका विस्तार करने के लिए किसी तीसरे पक्ष की आवश्यकता को पूरी तरह से दूर कर सकता हूं। ItemStackContainerभविष्य में भी विस्तारित किया जा सकता है, लेकिन उन तरीकों से नहीं जो मैं प्रदान कर सकता हूं, मेरे प्रदान किए गए डिफ़ॉल्ट कार्यान्वयन के बाहर।

अब, मैं इस पुस्तकालय को यथासंभव मजबूत बनाने की कोशिश कर रहा हूं; यह अभी भी प्रारंभिक अवस्था (प्री-प्री-अल्फा) में है, इसलिए यह ओवर इंजीनियरिंग (YAGNI) का एक कार्य हो सकता है। क्या यह जेनेरिक का उपयोग करने के लिए एक उपयुक्त स्थान है?

public interface ItemStack<T extends Item> {

    T getItem();

    int getQuantity();
}

तथा

public interface ItemStackFactory<T extends ItemStack<I extends Item>> {

    T createItemStack(I item, int quantity);
}

मुझे डर है कि यह कार्यान्वयन को समाप्त कर सकता है और पढ़ने और समझने में अधिक कठिन हो सकता है; मुझे लगता है कि जहां भी संभव हो, नेस्टिंग जेनरिक से बचने की सिफारिश की है।


1
किन्दा को लगता है कि आप कार्यान्वयन विवरण को किसी भी तरह से उजागर कर रहे हैं। कॉल करने वाले को सरल मात्रा के बजाय स्टैक के साथ काम करने और जानने / देखभाल करने की आवश्यकता क्यों है?
cHao

यह कुछ ऐसा है जिसके बारे में मैंने नहीं सोचा था। यह इस विशिष्ट समस्या में कुछ अच्छी जानकारी प्रदान करता है। मैं अपने कोड में परिवर्तन करना सुनिश्चित करूँगा।
ज़ीमस

जवाबों:


9

आप अपने इंटरफ़ेस में जेनरिक का उपयोग करते हैं जब आपका कार्यान्वयन सामान्य होने की संभावना है।

उदाहरण के लिए, कोई भी डेटा संरचना जो मनमानी वस्तुओं को स्वीकार कर सकती है, एक सामान्य इंटरफ़ेस के लिए एक अच्छा उम्मीदवार है। उदाहरण: List<T>और Dictionary<K,V>

कोई भी स्थिति जहां आप सामान्य तरीके से टाइप सुरक्षा में सुधार करना चाहते हैं, जेनरिक के लिए एक अच्छा उम्मीदवार है। ए List<String>एक सूची है जो केवल स्ट्रिंग्स पर संचालित होती है।

किसी भी स्थिति में जहां आप एसआरपी को सामान्यीकृत तरीके से लागू करना चाहते हैं और टाइप-विशिष्ट तरीकों से बचना चाहते हैं, जेनरिक के लिए एक अच्छा उम्मीदवार है। उदाहरण के लिए, एक व्यक्तिनिर्देशन, प्लेसडायरेक्मेंट और थिंगडक्वामेंटमेंट को ए से बदला जा सकता है Document<T>

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


मैं वास्तव में इस विचार से सहमत नहीं हूं कि जेनेरिक इंटरफेस को जेनेरिक कार्यान्वयन के साथ जोड़ा जाना चाहिए। एक इंटरफ़ेस में एक सामान्य प्रकार के पैरामीटर की घोषणा करना और फिर इसे विभिन्न कार्यान्वयनों में परिष्कृत या त्वरित करना एक शक्तिशाली तकनीक है। यह विशेष रूप से स्काला में आम है। सार बातें; कक्षाएं उन्हें विशेषज्ञ बनाती हैं।
बेंजामिन हॉजसन

@BenjaminHodgson: इसका मतलब है कि आप विशिष्ट प्रकार के लिए एक सामान्य इंटरफ़ेस पर कार्यान्वयन का उत्पादन कर रहे हैं। समग्र दृष्टिकोण अभी भी सामान्य है, हालांकि कुछ हद तक कम है।
रॉबर्ट हार्वे

मैं सहमत हूँ। शायद मैंने आपके जवाब को गलत समझा - आप उस तरह के डिजाइन के खिलाफ सलाह दे रहे थे।
बेंजामिन हॉजसन

@BenjaminHodgson: फैक्ट्री मेथड को जेनरिक की बजाए कंक्रीट इंटरफेस के खिलाफ लिखा जाएगा ? (हालांकि एक सार कारखाना सामान्य होगा)
रॉबर्ट हार्वे

मुझे लगता है कि हम सहमत हैं :) मैं शायद ओपी के उदाहरण को कुछ इस तरह लिखूंगा class StackFactory { Stack<T> Create<T>(T item, int count); }। यदि आप अधिक स्पष्टीकरण चाहते हैं, तो मैं एक उत्तर में "एक उपवर्ग में एक प्रकार के पैरामीटर को परिष्कृत करके" जो मेरा मतलब था, उसका एक उदाहरण लिख सकता हूं, हालांकि उत्तर केवल मूल प्रश्न से संबंधित होगा।
बेंजामिन हॉजसन

8

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

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

हालाँकि, आप जो कोशिश कर सकते हैं, वह आपके इंटरफेस और आपके आधार कार्यान्वयन के सामान्य और गैर-जेनेरिक दोनों संस्करणों को प्रदान करना है, ताकि जो लोग जेनरिक नहीं करते हैं वे अपनी संकीर्ण, टाइप-कास्ट-हैवी दुनिया में खुशी से रह सकें, जबकि लोग क्या सामान्य भाषा की व्यापक समझ के लाभों को पुनः प्राप्त कर सकते हैं।


3

अब, Itemऔर ItemStackFactoryमैं भविष्य में इसका विस्तार करने के लिए किसी तीसरे पक्ष की आवश्यकता को पूरी तरह से दूर कर सकता हूं।

वहाँ आपका निर्णय आपके लिए है। यदि आप जेनरिक प्रदान नहीं करते हैं तो उन्हें Itemहर बार अपने उपयोग के कार्यान्वयन के लिए अप-कास्ट की आवश्यकता होगी :

class MyItem implements Item {
}
MyItem item = new MyItem();
ItemStack is = createItemStack(item, 10);
// They would have to cast here.
MyItem theItem = (MyItem)is.getItem();

ItemStackआपको सुझाव देने के तरीके में कम से कम जेनेरिक बनाना चाहिए । जेनरिक का सबसे गहरा लाभ यह है कि आपको लगभग कभी भी कुछ भी डालने की आवश्यकता नहीं होती है, और कोड की पेशकश करना जो उपयोगकर्ताओं को डालने के लिए मजबूर करता है, IMHO एक आपराधिक अपराध है।


2

वाह ... मैं इस पर पूरी तरह से अलग है।

मैं पूरी तरह से कुछ तीसरे पक्ष की जरूरत के बारे में सोच सकता हूं

यह एक गलती है। आपको "भविष्य के लिए" कोड नहीं लिखना चाहिए।

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


0

मुझे लगता है कि आपकी स्थिति में जेनरिक का उपयोग करना बहुत जटिल है।

यदि आप इंटरफेस के जेनेरिक संस्करण को पसंद करते हैं, तो डिज़ाइन इंगित करता है

  1. ItemStackContainer <A> में एक ही प्रकार का स्टैक होता है, A. (यानी ItemStackContainer में कई प्रकार के स्टैक नहीं हो सकते हैं।)
  2. आइटमस्टैक एक विशेष प्रकार के आइटम के लिए समर्पित है। तो सभी प्रकार के ढेर का कोई अमूर्त प्रकार नहीं है।
  3. आपको प्रत्येक प्रकार के आइटम के लिए एक विशिष्ट ItemStackFactory परिभाषित करने की आवश्यकता है। इसे फैक्ट्री पैटर्न के रूप में बहुत अच्छा माना जाता है।
  4. अधिक कठिन कोड लिखें जैसे कि आइटमस्टैक <? आइटम> फैली हुई है, बनाए रखने में कमी।

इन निहित धारणाओं और प्रतिबंधों को ध्यान से तय करने के लिए बहुत महत्वपूर्ण चीजें हैं। इसलिए विशेष रूप से शुरुआती चरण में, इन समयपूर्व डिजाइनों को पेश नहीं किया जाना चाहिए। सरल, समझने में आसान, सामान्य डिजाइन वांछित है।


0

मैं कहेंगे यह मुख्य रूप से इस सवाल पर निर्भर करता है: किसी की कार्यक्षमता बढ़ाने के लिए की जरूरत है Itemऔर ItemStackFactory,

  • क्या वे सिर्फ उप-विधियों को अधिलेखित करेंगे और उनके उप-वर्गों के उदाहरणों का उपयोग आधार वर्गों की तरह ही किया जाएगा, बस अलग तरह से व्यवहार करें ?
  • या वे सदस्यों को जोड़ेंगे और उपवर्गों का अलग-अलग उपयोग करेंगे ?

पहले मामले में, जेनरिक की कोई आवश्यकता नहीं है, दूसरे में, संभवतः है।


0

हो सकता है, आपके पास एक इंटरफ़ेस पदानुक्रम हो, जहां फू एक इंटरफ़ेस है, बार एक और है। जब भी एक प्रकार दोनों होना चाहिए, यह एक FooAndBar है।

Desgin से बचने के लिए, केवल FooAndBar टाइप करें जब यह आवश्यक हो, और उन इंटरफेस की एक सूची रखें जो कभी भी कुछ भी नहीं करेंगे।

यह अधिकांश प्रोग्रामर्स के लिए बहुत अधिक आरामदायक है (जैसे कि उनके प्रकार की जाँच या किसी भी प्रकार के स्थिर टाइपिंग से निपटने के लिए पसंद नहीं है)। बहुत कम लोगों ने अपनी जेनेरिक क्लास लिखी है।

नोट के रूप में भी: इंटरफेस इस बारे में हैं कि संभावित कार्रवाई को कैसे चलाया जा सकता है। उन्हें वास्तव में क्रिया होना चाहिए न कि संज्ञा।


यह जेनरिक का उपयोग करने का एक विकल्प हो सकता है, लेकिन आपने जो सुझाव दिया था, उस पर जेनेरिक का उपयोग कब किया जाए , इस बारे में मूल प्रश्न । क्या आप सुझाव देने के लिए अपने उत्तर में सुधार कर सकते हैं कि जेनेरिक कभी आवश्यक नहीं हैं, लेकिन कई इंटरफेस का उपयोग करना सभी का जवाब है?
जे एलस्टन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.