सार्थक वर्ग के नामों के साथ जावा संग्रह को मास्क करने के लिए अच्छा या बुरा अभ्यास?


46

हाल ही में मुझे मानव-अनुकूल वर्ग नामों के साथ जावा संग्रह "मास्किंग" करने की आदत है। कुछ सरल उदाहरण:

// Facade class that makes code more readable and understandable.
public class WidgetCache extends Map<String, Widget> {
}

या:

// If you saw a ArrayList<ArrayList<?>> being passed around in the code, would you
// run away screaming, or would you actually understand what it is and what
// it represents?
public class Changelist extends ArrayList<ArrayList<SomePOJO>> {
}

एक सहकर्मी ने मुझे बताया कि यह बुरा व्यवहार है, और अंतराल / विलंबता का परिचय देता है, साथ ही साथ एक OO विरोधी पैटर्न भी है। मैं यह समझ सकता हूँ कि यह ओवरहेड प्रदर्शन की एक बहुत छोटी डिग्री है, लेकिन यह बिल्कुल भी महत्वपूर्ण नहीं है। तो मैं पूछता हूं: क्या यह अच्छा या बुरा है, और क्यों?


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

ChangeListसंकलन टूट जाएगा extends, क्योंकि सूची एक अंतरफलक है, की आवश्यकता है implements। @randomA क्या आप कल्पना कर रहे हैं इस त्रुटि की वजह से याद आती है
20:27 पर gnat

@gnat यह बात को अनदेखा कर नहीं है, मैं यह सोचते हैं कि वह एक विस्तार था implementationयानी HashMapया TreeMapऔर क्या वह लिखने में कोई त्रुटि थी।
सूचित

4
यह एक बीएडी अभ्यास है। बुरा बुरा बुरा। यह मत करो। हर कोई जानता है कि एक नक्शा <स्ट्रिंग, विजेट> क्या है। लेकिन एक विजेट? अब मुझे WidgetCache.java खोलने की आवश्यकता है, मुझे यह याद रखना होगा कि WidgetCache सिर्फ एक नक्शा है। मुझे हर बार यह जांचना पड़ता है कि मेरा नया संस्करण सामने आया है कि आपने कुछ नया नहीं बनाया है विजेटटैच पर। भगवान नहीं, ऐसा कभी नहीं।
माइल्स राउत

"अगर आपने किसी ArrayList <ArrayList <? >> को कोड में इधर-उधर देखा है, तो क्या आप चिल्लाते हुए भागेंगे?" नहीं, मैं नेस्टेड जेनरिक संग्रह के साथ काफी सहज हूं। और आपको होना भी चाहिए।
केविन Krumwiede

जवाबों:


75

अंतराल / विलंबता? मैं उस पर बीएस को फोन करता हूं। इस अभ्यास से बिल्कुल शून्य ओवरहेड होना चाहिए। ( संपादित करें: यह टिप्पणी में कहा गया है कि यह, वास्तव में, हॉटस्पॉट वीएम द्वारा किए गए अनुकूलन को रोक सकता है। मुझे इस बात की पुष्टि या इनकार करने के लिए वीएम कार्यान्वयन के बारे में पर्याप्त नहीं पता है। मैं अपनी टिप्पणी को C ++ से दूर कर रहा था। आभासी कार्यों का कार्यान्वयन।)

कुछ कोड ओवरहेड है। आपको अपने मापदंडों को अग्रेषित करते हुए, बेस क्लास से सभी कंस्ट्रक्टर बनाने होंगे।

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

इसके अलावा, संग्रह के मामले में, पैटर्न केवल इंटरफेस का उपयोग करने के सामान्य नियम के साथ मिलकर काम नहीं करता है, कार्यान्वयन नहीं है - अर्थात, सादे संग्रह कोड में, आप एक बनाएँगे HashMap<String, Widget>, और फिर इसे एक प्रकार के चर में असाइन करेंगे Map<String, Widget>। आपका WidgetCacheविस्तार नहीं हो सकता Map<String, Widget>, क्योंकि यह एक इंटरफ़ेस है। यह एक ऐसा इंटरफ़ेस नहीं हो सकता है जो आधार इंटरफ़ेस को विस्तारित करता है, क्योंकि HashMap<String, Widget>यह उस इंटरफ़ेस को लागू नहीं करता है, और न ही कोई अन्य मानक संग्रह करता है। और जब आप इसे एक वर्ग बना सकते हैं जो विस्तारित हो जाता है HashMap<String, Widget>, तो आपको तब चर के रूप में घोषित करना होगा WidgetCacheया Map<String, Widget>, और पहले आपको एक अलग संग्रह (शायद कुछ ORM का आलसी लोडिंग संग्रह) स्थानापन्न करने के लिए लचीलापन खो देता है, जबकि दूसरी तरह का हार बिंदु वर्ग का होना।

इनमें से कुछ प्रतिरूप मेरे प्रस्तावित विशेष वर्ग पर भी लागू होते हैं।

ये सभी विचार करने के बिंदु हैं। यह सही चुनाव हो भी सकता है और नहीं भी। किसी भी मामले में, आपके सहकर्मी के प्रस्तावित तर्क मान्य नहीं हैं। अगर उसे लगता है कि यह एक विरोधी पैटर्न है, तो उसे यह नाम देना चाहिए।


1
ध्यान दें कि हालांकि कुछ प्रदर्शन प्रभाव होना काफी संभव है, लेकिन यह भयानक नहीं है (कुछ inlining अनुकूलन को याद कर रहा है और सबसे खराब स्थिति में एक अतिरिक्त vcall प्राप्त कर रहा है - आपके अंतरतम लूप में महान नहीं है, लेकिन अन्यथा शायद ठीक है)।
वू

5
यह वास्तव में अच्छा जवाब सभी को बताता है "प्रदर्शन ओवरहेड द्वारा निर्णय का न्याय न करें" - और नीचे चर्चा केवल यह है कि "हॉटस्पॉट इसे कैसे संभालता है, क्या कुछ (सभी मामलों में 99.9%) अप्रासंगिक प्रदर्शन प्रभाव है" - दोस्तों, क्या आपने पहले वाक्य से अधिक पढ़ने की जहमत नहीं उठाई?
डॉक्टर ब्राउन

16
+1 के लिए "एक वर्ग बनाने के बजाय जो केवल नाम बदलने के लिए आधार वर्ग का निर्माण करता है, आप कैसे इसके बजाय एक वर्ग बनाते हैं जिसमें संग्रह होता है और एक केस-विशिष्ट, बेहतर इंटरफ़ेस प्रदान करता है?"
user11153

6
व्यावहारिक उदाहरण इस जवाब में मुख्य बिंदु को दर्शाता हुआ: प्रलेखन के लिए public class Properties extends Hashtable<Object,Object>में java.utilकहते हैं, "Hashtable, पुट और putAll तरीकों से गुण विरासत में मिली एक गुण वस्तु के लिए लागू किया जा सकता है क्योंकि उनके उपयोग अनुशंसित नहीं है के रूप में वे फोन करने वाले प्रविष्टियों जिसका कुंजी डाल सकते हैं। या मान स्ट्रिंग्स नहीं हैं। " रचना ज्यादा साफ-सुथरी होती।
पेट्रीसिया शहनहान

3
@DocBrown नहीं, यह उत्तर दावा करता है कि प्रदर्शन प्रभाव बिल्कुल नहीं है। यदि आप मजबूत बयान देते हैं तो आप बेहतर रूप से उनका समर्थन करने में सक्षम होंगे, अन्यथा लोग शायद आपको इस पर कॉल करेंगे। टिप्पणियों के पूरे बिंदु (उत्तरों पर) गलत तथ्यों या मान्यताओं को इंगित करने या उपयोगी नोट्स जोड़ने के लिए है, लेकिन निश्चित रूप से लोगों को बधाई देने के लिए नहीं है, यही वोटिंग सिस्टम के लिए है।
वू

25

आईबीएम के अनुसार यह वास्तव में एक प्रतिमान है। इन 'टाइपफ़ेड' जैसे वर्गों को प्यूसिडो प्रकार कहा जाता है।

लेख इसकी व्याख्या करता है कि मैं इससे बहुत बेहतर हूं, लेकिन लिंक के कम होने की स्थिति में मैं इसे संक्षेप में प्रस्तुत करने की कोशिश करूंगा:

  • कोई भी कोड जो उम्मीद करता है कि एक WidgetCacheसंभाल नहीं सकता हैMap<String, Widget>
  • ये Pseudotypes 'वायरल' होते हैं जब कई पैकेजों का उपयोग करते हुए वे असंगतता की ओर ले जाते हैं जबकि बेस प्रकार (बस एक मूर्खतापूर्ण नक्शा <...>) सभी मामलों में सभी पैकेजों में काम किया होगा।
  • छद्म प्रकार अक्सर ठोस होते हैं, वे विशिष्ट इंटरफेस को लागू नहीं करते हैं क्योंकि उनके आधार वर्ग केवल सामान्य संस्करण को लागू करते हैं।

लेख में वे छद्म प्रकारों का उपयोग किए बिना जीवन को आसान बनाने के लिए निम्नलिखित चाल का प्रस्ताव करते हैं:

public static <K,V> Map<K,V> newHashMap() {
    return new HashMap<K,V>(); 
}

Map<Socket, Future<String>> socketOwner = Util.newHashMap();

जो ऑटोमैटिक टाइप इंट्रेंस के कारण काम करता है।

(मैं इस संबंधित स्टैक ओवरफ्लो प्रश्न के माध्यम से इस जवाब पर आया )


13
क्या newHashMapहीरा ऑपरेटर द्वारा अब समाधान की आवश्यकता नहीं है ?
svick

बिल्कुल सच है, उस के बारे में भूल गया। मैं सामान्य रूप से जावा में काम नहीं करता।
रॉय टी।

काश, भाषाएं चर प्रकार के एक ब्रह्मांड की अनुमति देतीं, जो अन्य प्रकार के उदाहरणों के संदर्भ में होती हैं, लेकिन फिर भी संकलन-समय की जाँच का समर्थन कर सकती हैं, जैसे कि प्रकार का मान किसी प्रकार के WidgetCacheचर के लिए WidgetCacheया Map<String,Widget>बिना कलाकारों के सौंपा जा सकता है , लेकिन वहाँ एक ऐसा साधन था जिसके द्वारा कोई स्थिर विधि WidgetCacheकिसी संदर्भ को ले सकती है Map<String,Widget>और WidgetCacheकिसी भी वांछित सत्यापन के बाद इसे टाइप के रूप में वापस कर सकती है। ऐसी सुविधा जेनेरिक के साथ विशेष रूप से उपयोगी हो सकती है, जिसे टाइप-मिटाना नहीं होगा (...
सुपरकैट

... प्रकार केवल वैसे भी संकलक के दिमाग में मौजूद होंगे)। कई उत्परिवर्तित प्रकारों के लिए, दो सामान्य प्रकार के संदर्भ क्षेत्र होते हैं: एक जो केवल उदाहरण के लिए एकमात्र संदर्भ रखता है, जिसे उत्परिवर्तित किया जा सकता है, या एक उदाहरण के लिए एक परिमार्जन संदर्भ रखता है जिसे किसी को भी बदलने की अनुमति नहीं है। उन दो प्रकार के क्षेत्रों को अलग-अलग नाम देने में सक्षम होना उपयोगी होगा, क्योंकि उन्हें अलग-अलग उपयोग पैटर्न की आवश्यकता होती है, लेकिन उन्हें दोनों को एक ही प्रकार के ऑब्जेक्ट इंस्टेंस के संदर्भ में रखना चाहिए।
सुपरकैट

5
यह एक बहुत बड़ा तर्क है कि जावा को उपनाम की आवश्यकता क्यों है।
ग्लेनपेटर्सन

13

प्रदर्शन हिट अधिकतम दृश्यता पर सीमित होगा, जिसे आप सबसे अधिक संभावना पहले से ही कर रहे हैं। यह विरोध करने का एक वैध कारण नहीं है।

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

Typedefs के कई फायदे हैं। वे प्रोग्रामर के इरादे को स्पष्ट रूप से बेहतर नाम के साथ, अमूर्तता के अधिक उपयुक्त स्तर पर व्यक्त करते हैं। वे डीबगिंग या रीफैक्टरिंग उद्देश्यों के लिए खोज करना आसान है। हर जगह ढूँढने पर विचार WidgetCacheकिया जाता है बनाम उन विशिष्ट उपयोगों को खोजने के लिए उपयोग किया जाता है Map। उन्हें बदलना आसान है, उदाहरण के लिए अगर आपको बाद में पता चलता है कि आपको LinkedHashMapइसके बजाय कस्टम कंटेनर की जरूरत है।


कंस्ट्रक्टर्स में मिनिस्क्यूल ओवरहेड भी है।
स्टीफन सी।

11

मैं आपको सुझाव देता हूं, जैसा कि दूसरों ने पहले ही उल्लेख किया है, विरासत पर रचना का उपयोग करने के लिए, इसलिए आप केवल उन तरीकों को उजागर कर सकते हैं जिनकी वास्तव में जरूरत है, उन नामों के साथ जो इरादा उपयोग के मामले से मेल खाते हैं। क्या आपकी कक्षा के उपयोगकर्ताओं को वास्तव में यह जानना आवश्यक WidgetCacheहै कि नक्शा क्या है? और कुछ भी वे चाहते हैं के साथ यह करने में सक्षम हो? या उन्हें सिर्फ यह जानना चाहिए कि विगेट्स के लिए कैश क्या है?

उदाहरण के लिए मेरे कोडबेस से वर्ग, इसी तरह की समस्या के समाधान के साथ जिसे आपने छोड़ दिया है:

public class Translations {

    private Map<Locale, Properties> translations = new HashMap<>();

    public void appendMessage(Locale locale, String code, String message) {
        /* code */
    }

    public void addMessages(Locale locale, Properties messages) {
        /* code */
    }

    public String getMessage(Locale locale, String code) {
        /* code */
    }

    public boolean localeExists(Locale locale) {
        /* code */
    }
}

आप देख सकते हैं कि आंतरिक रूप से यह "सिर्फ एक नक्शा" है, लेकिन सार्वजनिक इंटरफ़ेस यह नहीं दिखा रहा है। और इसमें "प्रोग्रामर-फ्रेंडली" तरीके हैं जैसे appendMessage(Locale locale, String code, String message)नई प्रविष्टियाँ डालने का आसान और अधिक सार्थक तरीका। और वर्ग के उपयोगकर्ता ऐसा नहीं कर सकते, उदाहरण के लिए translations.clear(), क्योंकि Translationsइसका विस्तार नहीं है Map

वैकल्पिक रूप से, आप हमेशा आंतरिक रूप से उपयोग किए जाने वाले मानचित्र में कुछ आवश्यक तरीकों को सौंप सकते हैं।


6

मैं इसे एक सार्थक अमूर्तता के उदाहरण के रूप में देखता हूं। एक अच्छा अमूर्त लक्षण के एक जोड़े है:

  1. यह कार्यान्वयन विवरण को छुपाता है जो इसे उपभोग करने वाले कोड के लिए अप्रासंगिक हैं।

  2. यह केवल उतना ही जटिल है जितना इसे होना चाहिए।

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

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


4

यहां अन्य उत्तरों के लिए +1। मैं यह भी जोड़ूंगा कि यह वास्तव में डोमेन ड्रिवेन डिज़ाइन (DDD) समुदाय द्वारा बहुत अच्छा अभ्यास माना जाता है। वे इस बात की वकालत करते हैं कि आपके डोमेन और इसके साथ होने वाली बातचीत में अंतर्निहित डेटा संरचना के विपरीत अर्थ डोमेन होना चाहिए। एक Map<String, Widget>कैश हो सकता है, लेकिन यह कुछ और भी हो सकता है, जो आपने My Not So Humble Opinion (IMNSHO) में सही ढंग से किया है, वह यह है कि संग्रह क्या दर्शाता है, इस मामले में कैश है।

मैं इसमें एक महत्वपूर्ण संपादन जोड़ूंगा कि अंतर्निहित डेटा संरचना के आसपास के डोमेन वर्ग के आवरण में संभवतः अन्य सदस्य चर या फ़ंक्शन भी होने चाहिए जो वास्तव में इसे केवल डेटा संरचना के विपरीत इंटरैक्शन के साथ एक डोमेन वर्ग बनाते हैं (यदि केवल जावा में मूल्य प्रकार थे , हम उन्हें जावा 10 में मिलेगा - वादा! "

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

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