2010-02-25 14 views
69

में अत्यधिक दोहराव कोड और दस्तावेज़ीकरण का प्रबंधन करना बेहद दोहराव कोड आम तौर पर एक बुरी चीज है, और ऐसे डिज़ाइन पैटर्न हैं जो इसे कम करने में मदद कर सकते हैं। हालांकि, कभी-कभी भाषा की बाधाओं के कारण यह अनिवार्य है। प्रलेखन/विधि हस्ताक्षर में बहुत कम बदलाव लेकिन ठीक उसी विधि शरीर, एक जड़ से प्रत्येक के लिए साथजावा

/** 
* Assigns the specified long value to each element of the specified 
* range of the specified array of longs. The range to be filled 
* extends from index <tt>fromIndex</tt>, inclusive, to index 
* <tt>toIndex</tt>, exclusive. (If <tt>fromIndex==toIndex</tt>, the 
* range to be filled is empty.) 
* 
* @param a the array to be filled 
* @param fromIndex the index of the first element (inclusive) to be 
*  filled with the specified value 
* @param toIndex the index of the last element (exclusive) to be 
*  filled with the specified value 
* @param val the value to be stored in all elements of the array 
* @throws IllegalArgumentException if <tt>fromIndex &gt; toIndex</tt> 
* @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or 
*   <tt>toIndex &gt; a.length</tt> 
*/ 
public static void fill(long[] a, int fromIndex, int toIndex, long val) { 
    rangeCheck(a.length, fromIndex, toIndex); 
    for (int i=fromIndex; i<toIndex; i++) 
     a[i] = val; 
} 

ऊपर टुकड़ा स्रोत कोड में प्रकट होता है 8 बार,: java.util.Arrays से निम्न उदाहरण लें सरणी प्रकार int[], short[], char[], byte[], boolean[], double[], float[], और Object[]

मेरा मानना ​​है कि जब तक कोई प्रतिबिंबित नहीं करता है (जो स्वयं में एक पूरी तरह से अलग विषय है), यह पुनरावृत्ति अपरिहार्य है। मैं समझता हूं कि एक उपयोगिता वर्ग के रूप में, दोहराव वाले जावा कोड की उच्च सांद्रता अत्यधिक असामान्य है, लेकिन सर्वोत्तम अभ्यास के साथ भी, पुनरावृत्ति होती है! रिफैक्टरिंग हमेशा काम नहीं करती है क्योंकि यह हमेशा संभव नहीं होती है (स्पष्ट मामला तब होता है जब पुनरावृत्ति दस्तावेज में होती है)।

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

Google Research Blog - Extra, Extra - Read All About It: Nearly All Binary Searches and Mergesorts are Broken (by Joshua Bloch, Software Engineer)

बग क्या बहुत से विचार सिर्फ एक सरल और सीधा एल्गोरिथ्म होने के लिए होने वाली है, एक आश्चर्यजनक रूप से सूक्ष्म से एक है।

// int mid =(low + high)/2; // the bug 
    int mid = (low + high) >>> 1; // the fix 

ऊपर लाइन स्रोत कोड में 11 बार प्रकट होता है!

तो मेरी प्रश्न हैं:

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

एक टिप्पणी एक और उदाहरण का अनुरोध किया है, इसलिए मैं गूगल संग्रह से एक खींच लिया: com.google.common.base.Predicates लाइनों 276-310 (AndPredicate) लाइनों 312-346 (OrPredicate) बनाम।

इन दो वर्गों के लिए स्रोत, समान हैं के लिए छोड़कर:

  • AndPredicate बनाम OrPredicate
  • "And(" (प्रत्येक अपनी श्रेणी में 5 बार दिखाई देता है) (संबंधित toString() तरीकों में) बनाम Or("
  • #and बनाम #or (@see जावाडोक टिप्पणियों में)
  • true बनाम false (apply में; !hashCode()
+11

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

+2

+1 और http://netlib.bell-labs.com/cm/cs/pearls/ – stacker

+0

पूर्ण ओवरलोड प्रदान करने के कारण पुनरावृत्ति केवल एक उदाहरण है। मैंने गैर-ओवरलोडिंग और गैर-आदिम सरणी हैंडलिंग परिदृश्यों में भी इस तरह की पुनरावृत्ति देखी है। – polygenelubricants

उत्तर

31

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

एक ही समस्या प्रदर्शन कंप्यूटिंग जहां एक ही परिसर की जरूरत है नाव और डबल के लिए दोनों काम करने के लिए होता है (जैसे कि विधि Goldberd के "What every computer scientist should know about floating-point numbers" पेपर में दिखाया से कुछ)।

एक कारण है कि Trove का TIntIntHashMap इसी तरह के डेटा के साथ काम करते समय जावा के HashMap<Integer,Integer> के आसपास मंडल चलाता है।

अब ट्रोव संग्रह का स्रोत कोड कैसे लिखा गया है?

पाठ्यक्रम :) के स्रोत कोड उपकरण का उपयोग करके

उच्च प्रदर्शन है कि कोड जनरेटर का उपयोग दोहराया स्रोत कोड बनाने के लिए (डिफ़ॉल्ट जावा लोगों की तुलना में अधिक) के लिए कई जावा लाइब्रेरीज रहे हैं।

हम सभी जानते हैं कि "स्रोत कोड उपकरण" बुरा है और कोड जनरेशन बकवास है, लेकिन फिर भी लोग वास्तव में जानते हैं कि वे क्या कर रहे हैं (यानी ट्रॉव जैसी चीजें लिखने वाले लोग) ऐसा करते हैं:)

क्या इसके लायक है हम स्रोत कोड है कि जैसे बड़े चेतावनी शामिल उत्पन्न के लिए:

/* 
* This .java source file has been auto-generated from the template xxxxx 
* 
* DO NOT MODIFY THIS FILE FOR IT SHALL GET OVERWRITTEN 
* 
*/ 
+0

क्या आप कोड जनरेटर का उपयोग करने के बारे में अधिक जानकारी प्रदान कर सकते हैं, आदि? मैं ट्रोव से परिचित नहीं हूँ। – polygenelubricants

+1

यह ट्रोव एफएक्यू में समझाया गया है, मूल रूप से उनके पास एक चींटी लक्ष्य है जो एक स्क्रिप्ट को कॉल करता है जो संशोधन करता है (यदि मुझे सही याद है): http://trove4j.sourceforge.net/html/faq.html (I मैं जावा उच्च प्रदर्शन कंप्यूटिंग में हूं और मैंने कई बार तकनीक का उपयोग किया है ... हम इसका उपयोग यहां करते हैं, हमारे पास जावा जावा प्रोप्रायटरी कोड है जो अधिक जावा कोड उत्पन्न करता है :) – SyntaxT3rr0r

+1

@ पोलिजेनेलब्रिकेंट्स: बीटीडब्ल्यू ट्रोव एक अद्भुत प्रतिस्थापन है यदि आपको प्राइमेटिव्स के साथ काम करने की ज़रूरत है तो डिफ़ॉल्ट जावा एपीआई। नियमित संग्रह के लिए, आप जावोल्यूशन या Google संग्रह इत्यादि को देखना चाहेंगे। डिफ़ॉल्ट जावा संग्रह वास्तव में बहुत से स्टैंडपॉइंट्स से बहुत खराब हैं। यह सरल प्रोजेक्ट के लिए काम करता है लेकिन जब आप महत्वपूर्ण मात्रा में डेटा में हेरफेर करना शुरू करते हैं तो वे अपनी सीमाएं बहुत तेजी से दिखाते हैं। – SyntaxT3rr0r

1

में hashCode()

  • &= बनाम |= में अभिव्यक्ति में से)
  • -1 /* all bits on */ बनाम 0 /* all bits off */ में लिखा जा सकता पुनरावृत्ति के इस प्रकार का एक बहुत अब जेनरिक के लिए धन्यवाद बचा जा सकता है। वे एक ही कोड लिखते समय देवता हैं जहां केवल प्रकार बदलते हैं।

    अफसोस की बात है कि, मुझे लगता है कि जेनेरिक सरणी अभी भी बहुत अच्छी तरह से समर्थित नहीं हैं। कम से कम, अब कंटेनरों का उपयोग करें जो आपको जेनेरिक का लाभ लेने की अनुमति देते हैं। पॉलीमोर्फिज्म भी इस तरह के कोड डुप्लिकेशन को कम करने के लिए एक उपयोगी उपकरण है।

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

  • +3

    "अफसोस की बात है हालांकि, मुझे लगता है कि जेनेरिक सरणी अभी भी बहुत अच्छी तरह से समर्थित नहीं हैं।" - मुझे यकीन नहीं है कि आप टाइप-एरर के साथ जावा में जेनेरिक सरणी का समर्थन कैसे कर सकते हैं। मुझे लगता है कि यह असंभव है। – polygenelubricants

    +1

    मैंने एक कामकाज देखा है। ऑब्जेक्ट की सरणी कास्टिंग या प्रतिबिंब का उपयोग करना। कोई भी सुंदर नहीं है, लेकिन वे स्पष्ट रूप से काम करते हैं। – patros

    +0

    आम तौर पर, जावा में सरणी का उपयोग करने से बचने के लिए सबसे अच्छा है। ArrayLists एक बहुत अधिक कार्यक्षमता प्रदान करते हैं और आमतौर पर एक सरणी की तुलना में एक नगण्य प्रदर्शन लागत है। –

    16

    यदि आपको बिल्कुल कोड को डुप्लिकेट करना है, तो आपके द्वारा दिए गए महान उदाहरणों का पालन करें और उस कोड को उस स्थान पर समूहित करें जहां आपको ढूंढना आसान है और जब आपको कोई परिवर्तन करना है तो ठीक करना आसान है। डुप्लिकेशन को दस्तावेज करें और, सबसे महत्वपूर्ण बात यह है कि डुप्लिकेशंस के कारण का कारण है ताकि आपके पीछे आने वाले सभी को दोनों के बारे में पता हो।

    +1

    +1 डुप्लीकेट दस्तावेज करके डुप्लीकेट दस्तावेज की लंबाई में वृद्धि करना प्रतीत होता है कि यह पहले एक बुरा विचार हो सकता है, लेकिन डुप्लीकेट किए गए सामानों को वास्तव में बहुत खराब करना है जिन्हें संशोधित करने की आवश्यकता है और डुप्लिकेशन के बारे में कोई दस्तावेज नहीं है। – Tanzelax

    6

    Wikipedia से अपने आप को (सूखी) दोहराएँ मत करो या दोहराव ईविल (DIE)

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

    ऐसी समस्याएं रोकने के लिए शायद कोई जवाब या तकनीक नहीं है।

    2

    मुझे लगता है कि सूर्य को जावा एसई लाइब्रेरी कोड के लिए इस तरह दस्तावेज़ करना होगा और शायद अन्य तृतीय पक्ष लाइब्रेरी लेखकों को भी ऐसा करना होगा।

    हालांकि, मुझे लगता है कि यह इस तरह की फाइल में दस्तावेज़ की प्रतिलिपि बनाने और पेस्ट करने के लिए एक पूरी तरह बर्बाद है जो केवल घर में उपयोग किया जाता है।मुझे पता है कि बहुत से लोग असहमत होंगे क्योंकि इससे घर में जावाडॉक्स कम साफ दिखेंगे। हालांकि, व्यापार बंद यह है कि उनके कोड को और अधिक साफ कर देता है, जो मेरी राय में, अधिक महत्वपूर्ण है।

    +0

    +1 आप क्लास जावाडोक में एक बार प्रत्येक डुप्लीकेट विधि के लिए प्रलेखन लिख सकते हैं और एक छोटी विधि जावाडोक का उपयोग कर सकते हैं जो "क्लास जावाडोक देखें" –

    2

    जावा आदिम प्रकार आपको स्क्रू करते हैं, खासकर जब यह सरणी की बात आती है। यदि आप विशेष रूप से प्राचीन प्रकार से जुड़े कोड के बारे में पूछ रहे हैं, तो मैं कहूंगा कि बस उनसे बचने का प्रयास करें। यदि आप बॉक्स किए गए प्रकारों का उपयोग करते हैं तो ऑब्जेक्ट [] विधि पर्याप्त है।

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

    2

    आप टेम्पलेट का उपयोग कर कोड के विविधताएं बनाने के लिए कोड जेनरेटर का उपयोग कर सकते हैं। उस स्थिति में, जावा स्रोत जनरेटर का एक उत्पाद है और वास्तविक कोड टेम्पलेट है।

    +0

    हां, यही वह बात है जब मैंने कहा कि शायद सूर्य का अपना प्रीप्रोसेसर है , आदि – polygenelubricants

    +1

    ऐसा करने के लिए आधिकारिक रूप से स्वीकृत तरीका एक एनोटेशन और एनोटेशन प्रोसेसर का उपयोग करना होगा ताकि जब आप कोड संकलित करते हैं, तो जावैक आपके एनोटेशन प्रोसेसर को कॉल करेगा जो बदले में फ्लाई पर स्रोत कोड उत्पन्न करेगा संकलक। ऐसा करने का अनौपचारिक तरीका यह है कि आपका एनोटेशन प्रोसेसर आंतरिक कंपाइलर डेटा संरचनाओं को संशोधित करते समय संशोधित करें। मुझे मिली एकमात्र मुक्त जावा स्रोत जनरेशन लाइब्रेरी कोडमोडेल है। – msalib

    +0

    यह बड़े स्निपेट के लिए उचित लगता है, लेकिन थोड़ा सा प्रतिकृति निर्माण प्रक्रिया में जटिलता की एक और परत जोड़ने के लिए दो बुराइयों में से कम हो सकता है। – dsimcha

    2

    कपोल-कल्पना है कि कोड को एकजुट के निर्माण के लिए दो कोड के टुकड़े कि इसी तरह की होने का दावा किया जाता है को देखते हुए, सबसे अधिक भाषाओं सीमित है सुविधाएं एक monolith में टुकड़े। अमूर्त करने के लिए जब आपकी भाषा इसे नहीं कर सकती है, तो आपको भाषा के बाहर कदम उठाना होगा: - {

    सबसे सामान्य "अमूर्त" तंत्र एक पूर्ण मैक्रो प्रोसेसर है जो तत्काल "मैक्रो बॉडी" के लिए मनमानी गणनाओं को लागू कर सकता है यह (Post or string-rewriting सिस्टम सोचें, जो ट्यूरिंग सक्षम है)। M4 और GPM उत्कृष्ट उदाहरण हैं। सी प्रीप्रोसेसर इनमें से एक नहीं है।

    यदि आपके पास ऐसा मैक्रो प्रोसेसर है, तो आप मैक्रो के रूप में "अमूर्त" बना सकते हैं, और आपके "सारणित" स्रोत टेक्स्ट पर मैक्रो प्रोसेसर चला सकते हैं ताकि आप वास्तविक स्रोत कोड को संकलित और चला सकें।

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

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

    इस तरह के एक उपकरण को Program Transformation System कहा जाता है। ऐसा टूल एक कंपाइलर की तरह स्रोत टेक्स्ट को पार करता है, और उसके बाद वांछित प्रभाव प्राप्त करने के लिए विश्लेषण/परिवर्तन करता है। यदि आप प्रोग्राम प्रोग्राम ट्रांसफॉर्मेशन टूल को निर्देशित करने के लिए अपने प्रोग्राम के स्रोत टेक्स्ट में मार्कर डाल सकते हैं (उदाहरण के लिए, लैंगुग में संरचित टिप्पणियां या एनोटेशन) जो करना है, तो आप इसका उपयोग ऐसे अमूर्त त्वरण, कोड जनरेशन और करने के लिए कर सकते हैं।/या कोड अनुकूलन। (जावा कंपाइलर में हुकिंग का एक पोस्टर का सुझाव इस विचार पर एक भिन्नता है)। । एक सामान्य puprose परिवर्तन प्रणाली का उपयोग करना (DMS Software Reengineering Tookit का मतलब है इस तरह के रूप में आप इस अनिवार्य रूप से किसी भी भाषा के लिए

    4

    हास्केल की तरह यहां तक ​​कि फैंसी पैंट भाषाओं दोहराए कोड है क्या कर सकते हैं (see my post on haskell and serialization)

    ऐसा लगता है इस समस्या का तीन विकल्प हैं: खाका हास्केल या Caml4p अपनी भाषा के लिए बराबर है और गंदगी के साथ रहती तरह

    1. उपयोग प्रतिबिंब और खो प्रदर्शन
    2. उपयोग preprocessing
    3. या मेरी निजी पसंदीदा उपयोग मैक्रो अपनी भाषा में यह (योजना, और तुतलाना)

    मैं मैक्रो preprocessing से अलग करने पर विचार क्योंकि मैक्रो एक ही भाषा में आम तौर पर कर रहे हैं का समर्थन करता है कि लक्ष्य है जहां पूर्व प्रसंस्करण के रूप में एक अलग है भाषा।

    मुझे लगता है कि लिस्प/स्कीम मैक्रोज़ इन समस्याओं में से कई को हल करेगा।

    +2

    +1। –