क्या (ओवर) परावर्तन का उपयोग करना एक बुरी आदत है?


16

क्या बॉयलरप्लेट कोड की मात्रा को बहुत कम करने पर प्रतिबिंब का उपयोग करना एक अच्छा अभ्यास है?

मूल रूप से प्रदर्शन के बीच एक व्यापार-बंद है और एक तरफ पठनीयता और दूसरी तरफ बॉयलरप्लेट कोड का अमूर्त / स्वचालन / कमी है।

संपादित करें: यहाँ प्रतिबिंब के अनुशंसित उपयोग का एक उदाहरण है

एक उदाहरण देने के लिए, मान लीजिए कि एक अमूर्त वर्ग है Baseजिसमें 10 फ़ील्ड हैं और 3 उपवर्ग हैं SubclassA, SubclassBऔर SubclassCप्रत्येक में 10 अलग-अलग फ़ील्ड हैं; वे सभी सरल सेम हैं। समस्या यह है कि आपको दो Baseप्रकार के संदर्भ मिलते हैं और आप देखना चाहते हैं कि क्या उनकी संबंधित वस्तुएं समान (उप) प्रकार की हैं और समान हैं।

समाधान के रूप में कच्चा घोल होता है जिसमें आप पहले जांचते हैं कि क्या प्रकार बराबर हैं और फिर सभी फ़ील्ड्स की जाँच करें या आप प्रतिबिंब का उपयोग कर सकते हैं और गतिशील रूप से देख सकते हैं कि क्या वे एक ही प्रकार के हैं और सभी तरीकों पर पुनरावृति करते हैं जो "प्राप्त" से शुरू होते हैं (सम्मेलन) ओवर कॉन्फ़िगरेशन), उन्हें दोनों वस्तुओं पर कॉल करें और परिणामों पर बराबर कॉल करें।

boolean compare(Base base1, Base, base2) {
    if (base1 instanceof SubclassA && base2 instanceof SubclassA) { 
         SubclassA subclassA1 = (SubclassA) base1;
         SubclassA subclassA2 = (SubclassA) base2;
         compare(subclassA1, subclassA2);
    } else if (base1 instanceof SubclassB && base2 instanceof SubclassB) {
         //the same
    }
    //boilerplate
}

boolean compare(SubclassA subA1, SubclassA subA2) {
    if (!subA1.getField1().equals(subA2.getField1)) {
         return false;
    }
    if (!subA1.getField2().equals(subA2.getField2)) {
         return false;
    }
    //boilerplate
}

boolean compare(SubclassB subB1, SubclassB subB2) {
    //boilerplate
}

//boilerplate

//alternative with reflection 
boolean compare(Base base1, Base base2) {
        if (!base1.getClass().isAssignableFrom(base2.getClass())) {
            System.out.println("not same");
            System.exit(1);
        }
        Method[] methods = base1.getClass().getMethods();
        boolean isOk = true;
        for (Method method : methods) {
            final String methodName = method.getName();
            if (methodName.startsWith("get")) {
                Object object1 = method.invoke(base1);
                Object object2 = method.invoke(base2);
                if(object1 == null || object2 == null)  {
                    continue;
                }
                if (!object1.equals(object2)) {
                    System.out.println("not equals because " + object1 + " not equal with " + object2);
                    isOk = false;
                }
            }
        }

        if (isOk) {
            System.out.println("is OK");
        }
}

20
किसी भी चीज का अति प्रयोग एक बुरी आदत है।
ट्यूलेंस कोर्डोवा

1
@ user61852 अधिकार, बहुत अधिक स्वतंत्रता तानाशाही की ओर ले जाती है। कुछ पुराने ग्रीक इस बारे में पहले से ही जानते थे।
ott--

6
“बहुत ज्यादा पानी आपके लिए बुरा होगा। जाहिर है, बहुत अधिक मात्रा ठीक है कि मात्रा जो अत्यधिक है - यही इसका मतलब है! ”- स्टीफन फ्राई
जॉन पर्ड

4
"कभी भी आप स्वयं को फ़ॉर्म का कोड लिखते हुए पाते हैं" यदि ऑब्जेक्ट टाइप T1 का है, तो कुछ करें, लेकिन यदि वह टाइप T2 का है, तो कुछ और करें, "अपने आप को थप्पड़ मारें
rm5248

जवाबों:


25

एक विशिष्ट उद्देश्य के लिए परावर्तन का निर्माण किया गया था, एक ऐसे वर्ग की कार्यक्षमता की खोज करने के लिए जो संकलन के समय अज्ञात था, सी dlopenऔर dlsymफंक्शन के समान क्या है। इसके बाहर किसी भी तरह के उपयोग की भारी छानबीन की जानी चाहिए ।

क्या आपके साथ कभी ऐसा हुआ है कि जावा डिजाइनरों ने स्वयं इस समस्या का सामना किया हो? इसलिए व्यावहारिक रूप से हर वर्ग के पास एक equalsतरीका है। विभिन्न वर्गों में समानता की अलग-अलग परिभाषा है। कुछ परिस्थितियों में एक व्युत्पन्न वस्तु एक आधार वस्तु के बराबर हो सकती है। कुछ परिस्थितियों में, बिना गेटर्स के निजी क्षेत्रों के आधार पर समानता का निर्धारण किया जा सकता है। तुम्हें पता नहीं है।

इसीलिए हर वस्तु जो कस्टम समानता चाहती है, एक equalsविधि को लागू करना चाहिए । आखिरकार, आप वस्तुओं को एक सेट में रखना चाहते हैं, या उन्हें हैश इंडेक्स के रूप में उपयोग करना चाहते हैं, फिर आपको equalsवैसे भी लागू करना होगा । अन्य भाषाएं इसे अलग तरह से करती हैं, लेकिन जावा उपयोग करता है equals। आपको अपनी भाषा के सम्मेलनों से चिपके रहना चाहिए।

इसके अलावा, "बॉयलरप्लेट" कोड, अगर सही कक्षा में रखा जाता है, तो पेंच करना बहुत कठिन है। चिंतन अतिरिक्त जटिलता जोड़ता है, जिसका अर्थ है बग के लिए अतिरिक्त संभावना। उदाहरण के लिए, यदि एक nullनिश्चित क्षेत्र के लिए कोई रिटर्न देता है और दूसरे को नहीं तो दो वस्तुओं को समान माना जाता है । क्या होगा यदि आपका एक गेटर्स आपकी एक वस्तु को बिना किसी उचित वस्तु के लौटा दे equals? आपकी if (!object1.equals(object2))असफलता होगी। इसके अलावा इसे बग प्रोन बनाने का तथ्य यह है कि प्रतिबिंब का उपयोग शायद ही कभी किया जाता है, इसलिए प्रोग्रामर इसके गोचरों से परिचित नहीं हैं।


12

प्रतिबिंब का अति प्रयोग संभवतः प्रयुक्त भाषा पर निर्भर करता है। यहां आप जावा का उपयोग कर रहे हैं। उस मामले में, प्रतिबिंब का उपयोग देखभाल के साथ किया जाना चाहिए क्योंकि अक्सर यह केवल खराब डिजाइन के लिए एक समाधान है।

तो, आप विभिन्न वर्गों की तुलना कर रहे हैं, यह विधि ओवरराइडिंग के लिए एक सही समस्या है । ध्यान दें कि दो अलग-अलग वर्गों के उदाहरणों को कभी समान नहीं माना जाना चाहिए। आप समानता के लिए तुलना तभी कर सकते हैं जब आपके पास एक ही वर्ग के उदाहरण हों। उदाहरण के लिए /programming/27581/overriding-equals-and-hashcode-in-java देखें कि समानता तुलना को सही तरीके से कैसे लागू किया जाए।


16
+1 - हम में से कुछ का मानना ​​है कि प्रतिबिंब का कोई भी उपयोग एक लाल झंडा है जो खराब डिज़ाइन को दर्शाता है।
रॉस पैटरसन

1
शायद इस मामले में बराबरी पर आधारित एक समाधान वांछनीय है, लेकिन एक सामान्य विचार के रूप में प्रतिबिंब समाधान के साथ क्या गलत है? वास्तव में यह बहुत सामान्य है और बराबरी के तरीकों को हर वर्ग और उपवर्ग में स्पष्ट रूप से लिखे जाने की आवश्यकता नहीं है (हालांकि वे आसानी से एक अच्छे आईडीई द्वारा उत्पन्न किए जा सकते हैं)।
m3th0dman 11

2
@RossPatterson क्यों?
m3th0dman 11

2
@ m3th0dman क्योंकि यह आपकी compare()पद्धति जैसी चीजों की ओर ले जाता है, जो मानती हैं कि "get" से शुरू होने वाली कोई भी विधि एक गेटर है और तुलनात्मक ऑपरेशन के हिस्से के रूप में कॉल करने के लिए सुरक्षित और उपयुक्त है। यह ऑब्जेक्ट की इच्छित इंटरफ़ेस परिभाषा का उल्लंघन है, और जबकि यह समीचीन हो सकता है, यह लगभग हमेशा गलत है।
रॉस पैटरसन

4
@ m3th0dman यह एनकैप्सुलेशन का उल्लंघन करता है - बेस क्लास को अपने उपवर्गों में विशेषताओं का उपयोग करना पड़ता है। यदि वे निजी हैं (या गेट्टर प्राइवेट) तो क्या होगा? बहुरूपता के बारे में क्या? अगर मैं एक और उपवर्ग जोड़ने का फैसला करता हूं और मैं तुलना अलग तरीके से करना चाहता हूं? खैर, बेस क्लास पहले से ही मेरे लिए कर रहा है और मैं इसे बदल नहीं सकता। क्या होगा अगर गेटर्स आलसी-लोड कुछ करते हैं? क्या मुझे ऐसा करने के लिए तुलना विधि चाहिए? मुझे कैसे पता चलेगा कि एक विधि शुरू होती getहै जो एक गटर है न कि एक कस्टम विधि जो कुछ लौटाती है?
सुल्तान

1

मुझे लगता है कि आपके यहां दो मुद्दे हैं।

  1. मेरे पास कितना गतिशील बनाम स्थिर कोड होना चाहिए?
  2. मैं समानता का एक कस्टम संस्करण कैसे व्यक्त करूं?

डायनेमिक बनाम स्टेटिक कोड

यह एक बारहमासी प्रश्न है और इसका उत्तर बहुत ही स्पष्ट है।

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

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

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

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

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

कस्टम समानता

जबकि मैं समझता हूं कि जावा equalsआम तौर पर दो वस्तुओं की तुलना करने के लिए विधि पर बहुत अधिक निर्भर करता है जो किसी दिए गए संदर्भ में हमेशा उपयुक्त नहीं होता है।

एक और तरीका है, और यह एक जावा मानक भी है। इसे एक तुलनित्र कहा जाता है ।

जैसा कि आप अपने तुलनित्र को कैसे लागू करते हैं, इस बात पर निर्भर करेगा कि आप क्या तुलना कर रहे हैं, और कैसे।

  • यह उनके विशिष्ट equalsकार्यान्वयन की परवाह किए बिना किसी भी दो वस्तुओं पर लागू किया जा सकता है ।
  • यह किसी भी दो वस्तुओं को संभालने के लिए एक सामान्य (प्रतिबिंब आधारित) तुलना विधि को लागू कर सकता है।
  • बॉयलरप्लेट तुलनात्मक कार्यों को आमतौर पर तुलना की गई वस्तु प्रकारों के लिए जोड़ा जा सकता है, प्रकार की सुरक्षा और अनुकूलन प्रदान करता है।

1

मैं जितना हो सके चिंतनशील प्रोग्रामिंग से बचना पसंद करता हूं क्योंकि यह

  • कोड को संकलक द्वारा सांख्यिकीय रूप से जांचना कठिन बनाता है
  • कोड के बारे में तर्क करना कठिन बनाता है
  • कोड को रिफलेक्टर के लिए कठिन बनाता है

यह सरल विधि कॉल की तुलना में बहुत कम प्रदर्शन करने वाला है; यह परिमाण या अधिक के क्रम से धीमा हुआ करता था।

स्टेटिकली चेकिंग कोड

कोई भी चिंतनशील कोड स्ट्रिंग का उपयोग करके कक्षाओं और विधियों को देखता है; मूल उदाहरण में यह "get" से शुरू होने वाली किसी भी विधि की तलाश में है; यह गेटर्स लौटाएगा लेकिन इसके अलावा अन्य तरीके, जैसे "gettysburgAddress ()"। नियमों को कोड में कड़ा किया जा सकता है, लेकिन बिंदु यह है कि यह एक रनटाइम चेक है ; एक आईडीई और कंपाइलर मदद नहीं कर सकता। सामान्य तौर पर मैं "कड़ाई से टाइप" या "आदिम जुनूनी" कोड को नापसंद करता हूं।

कारण के बारे में कठिन

चिंतनशील कोड सरल विधि कॉल की तुलना में अधिक क्रिया है। अधिक कोड = अधिक बग, या बग के लिए कम से कम अधिक क्षमता, पढ़ने के लिए अधिक कोड, परीक्षण करने के लिए कम आदि।

रिफ्लेक्टर के लिए कठिन

क्योंकि कोड कड़ाई से / गतिशील रूप से आधारित है; जैसे ही प्रतिबिंब उस दृश्य पर होता है जिसे आप IDE के रीफैक्टरिंग टूल का उपयोग करके 100% आत्मविश्वास के साथ कोड को रिफलेक्टर नहीं कर सकते क्योंकि IDE रिफ्लेक्टिव उपयोग नहीं कर सकता।

मूल रूप से, यदि संभव हो तो सामान्य कोड में प्रतिबिंब से बचें; एक बेहतर डिजाइन के लिए देखो।


0

परिभाषा के अनुसार किसी भी चीज का अति प्रयोग बुरा है, है ना? तो आइए अब इसके लिए (ओवर) से छुटकारा पाएं।

क्या आप वसंत के आश्चर्यजनक भारी आंतरिक उपयोग को एक बुरी आदत कहेंगे?

एनोटेशन का उपयोग करके वसंत की झलक दिखाई देती है - इसलिए हाइबरनेट (और शायद दर्जनों / सैकड़ों अन्य उपकरण)।

यदि आप इसे अपने कोड में उपयोग करते हैं तो उन पैटर्नों का पालन करें। अपने उपयोगकर्ता की IDE सुनिश्चित करने के लिए एनोटेशन का उपयोग करें फिर भी उनकी मदद कर सकते हैं (भले ही आप अपने कोड के केवल "उपयोगकर्ता" हों, प्रतिबिंब का लापरवाह उपयोग शायद आपको बट में अंततः काटने के लिए वापस आएगा)।

हालाँकि इस बात पर ध्यान दिए बिना कि आपके कोड का उपयोग डेवलपर्स द्वारा भी किया जाएगा, यहां तक ​​कि प्रतिबिंब का सबसे सरल उपयोग संभवतः अधिक उपयोग है।


0

मुझे लगता है कि इन जवाबों में से अधिकांश बिंदु याद आते हैं।

  1. हां, आपको शायद @KarlBielefeldt द्वारा लिखा equals()और लिखा जाना चाहिए hashcode()

  2. लेकिन, कई क्षेत्रों के साथ एक वर्ग के लिए, यह थकाऊ बायलरप्लेट हो सकता है।

  3. तो, यह निर्भर करता है

    • आप केवल बराबरी और hashCode की जरूरत है शायद ही कभी , यह एक सामान्य प्रयोजन प्रतिबिंब गणना उपयोग करने के लिए व्यावहारिक है, और शायद ठीक है,। कम से कम एक त्वरित और गंदे के रूप में पहले पास।
    • लेकिन अगर आपको बहुत कुछ करने की ज़रूरत है, जैसे कि इन वस्तुओं को हैशटेबल्स में डाल दिया जाए, ताकि प्रदर्शन एक मुद्दा बन जाए, तो आपको निश्चित रूप से कोड लिखना चाहिए। यह तेजी से रास्ता होगा।
  4. एक और संभावना: यदि आप कक्षाओं में वास्तव में इतने सारे क्षेत्र हैं कि एक बराबर लिखना थकाऊ है, तो विचार करें

    • खेतों को एक मानचित्र में रखना।
    • आप अभी भी कस्टम गेटर्स और सेटर्स लिख सकते हैं
    • Map.equals()अपनी तुलना के लिए उपयोग करें। (या Map.hashcode())

उदाहरण के लिए ( ध्यान दें : अशक्त जांचों को अनदेखा करते हुए, संभवतः स्ट्रिंग कुंजी के बजाय Enums का उपयोग करना चाहिए, बहुत अधिक नहीं दिखाया गया है ...)

class TooManyFields {
  private HashMap<String, Object> map = new HashMap<String, Object>();

  public setFoo(int i) { map.put("Foo", Integer.valueOf(i)); }
  public int getFoo()  { return map.get("Foo").intValue(); }

  public setBar(Sttring s) { map.put("Bar", s); }
  public String getBar()  { return map.get("Bar").toString(); }

  ... more getters and setters ...

  public boolean equals(Object o) {
    return (o instanceof TooManyFields) &&
           this.map.equals( ((TooManyFields)o).map);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.