घोंसले के शिकार को कम करने के लिए "यदि" कथन को पलट दें


272

जब मैंने अपने कोड पर ReSharper भाग लिया , उदाहरण के लिए:

    if (some condition)
    {
        Some code...            
    }

ReSharper ने मुझे उपरोक्त चेतावनी दी (यदि "नेस्टिंग कम करने के लिए बयान"), और निम्नलिखित सुधार का सुझाव दिया:

   if (!some condition) return;
   Some code...

मैं समझना चाहूंगा कि क्यों बेहतर है। मैंने हमेशा सोचा कि एक विधि समस्याग्रस्त के बीच में "वापसी" का उपयोग करते हुए, कुछ हद तक "गोटो" की तरह।


1
मेरा मानना ​​है कि शुरुआत में अपवाद की जाँच और वापसी ठीक है, लेकिन मैं इस स्थिति को बदल दूंगा ताकि आप अपवाद के लिए सीधे जाँच कर रहे हैं न कि कुछ (यानी अगर (कुछ संक्षेप) वापसी)।
bruceatk

36
नहीं, यह प्रदर्शन के लिए कुछ नहीं करेगा।
सेठ कार्नेगी

3
अगर मेरे तरीके के बजाय खराब डेटा पास किया जा रहा था, तो मुझे आर्ग्युमेंट एक्ससेप्शन फेंकने का लालच होगा।
asawyer

1
@asawyer हाँ, ऐसे कार्यों के बारे में यहाँ एक पूरी चर्चा है जो बकवास इनपुटों को माफ कर रहे हैं - जैसा कि एक विफलता का उपयोग करने के लिए विरोध किया गया है। सॉलिड कोड लिखने से मेरी आंखें खुल गईं । किस मामले में, यह कुछ इस तरह होगा ASSERT( exampleParam > 0 )
ग्रेग हेंडरशॉट

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

जवाबों:


296

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

double getPayAmount() {
    double result;
    if (_isDead) result = deadAmount();
    else {
        if (_isSeparated) result = separatedAmount();
        else {
            if (_isRetired) result = retiredAmount();
            else result = normalPayAmount();
        };
    }
     return result;
};

इस मामले में, अगर _isDeadयह सच है, तो हम तुरंत विधि से बाहर निकल सकते हैं। इसके बजाय इसे इस तरह से तैयार करना बेहतर हो सकता है:

double getPayAmount() {
    if (_isDead)      return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired)   return retiredAmount();

    return normalPayAmount();
};   

मैंने इस कोड को रिफैक्टिंग कैटलॉग से उठाया है । इस विशिष्ट रीफैक्टरिंग को कहा जाता है: रिप्लेस्ड नेस्टेड कंडीशनल विथ गार्ड क्लॉज़।


13
यह एक बहुत अच्छा उदाहरण है! Refactored कोड एक केस स्टेटमेंट की तरह अधिक पढ़ता है।
अन्य लोग

16
शायद सिर्फ स्वाद की बात है: मैं 2 और 3 को बदलने का सुझाव देता हूं "अगर" और "अगर" तो पठनीयता को और भी अधिक बढ़ाने के लिए। यदि कोई "वापसी" कथन को अनदेखा करता है, तो यह अभी भी स्पष्ट होगा कि निम्न मामले की जाँच केवल तभी की जाती है यदि पिछले वाला विफल हो गया हो, अर्थात चेक का क्रम महत्वपूर्ण हो।
foraidt

2
एक उदाहरण में यह सरल, आईडी सहमत है कि दूसरा तरीका बेहतर है, लेकिन केवल इसलिए कि इसका एसओ स्पष्ट रूप से हो रहा है।
एंड्रयू बुलॉक

अब, हम उस सुंदर कोड jop को कैसे लेते हैं और दृश्य स्टूडियो को इसे तोड़ने और अलग-अलग लाइनों पर रिटर्न डालने से रोकते हैं? यह वास्तव में मुझे परेशान करता है कि यह इस तरह से कोड को सुधारता है। यह बहुत ही पठनीय कोड बनाता है।
मार्क टी

@ मर्क टी दृश्य स्टूडियो में सेटिंग्स हैं जो इसे उस कोड को तोड़ने से रोकने के लिए हैं।
एरॉन स्मिथ

333

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

दूसरी ओर, यह आपके तरीके को कई निकास बिंदु भी बनाता है, ऐसा कुछ जो लोगों के एक अन्य समूह का मानना ​​है कि नहीं-नहीं है।

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

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


41
एकल निकास बिंदु? इसकी जरूरत किसे है?
sq33G

3
@ sq33G: SESE पर यह प्रश्न (और पाठ्यक्रम के उत्तर) शानदार हैं। लिंक के लिए धन्यवाद!
जॉन

मैं जवाबों में सुनता रहता हूं कि कुछ लोग ऐसे हैं जो एकल निकास बिंदुओं की वकालत करते हैं, लेकिन मैंने कभी किसी को वास्तव में वकालत करते नहीं देखा, खासकर सी # जैसी भाषाओं में।
थॉमस बोनीनी

1
@AndreasBonini: अभाव का प्रमाण अनुपस्थिति का प्रमाण नहीं है। :-)
जॉन

हाँ, मुझे यह अजीब लगता है कि हर किसी को यह कहने की आवश्यकता महसूस होती है कि कुछ लोगों को एक और दृष्टिकोण पसंद है अगर उन लोगों को इसे स्वयं कहने की आवश्यकता महसूस नहीं होती है =)
थॉमस बोनिनी

102

यह थोड़ा धार्मिक तर्क है, लेकिन मैं ReSharper से सहमत हूं कि आपको कम घोंसले के शिकार को प्राथमिकता देनी चाहिए। मेरा मानना ​​है कि यह एक फ़ंक्शन से कई वापसी पथ होने की नकारात्मकताओं को दूर करता है।

कम नेस्टिंग होने का मुख्य कारण कोड की पठनीयता और रखरखाव में सुधार है । याद रखें कि कई अन्य डेवलपर्स को भविष्य में आपके कोड को पढ़ने की आवश्यकता होगी, और कम इंडेंटेशन वाले कोड को पढ़ने के लिए आम तौर पर बहुत आसान है।

Preconditions एक शानदार उदाहरण है जहां फ़ंक्शन की शुरुआत में वापस लौटना ठीक है। प्रीऑन्डिशन चेक की उपस्थिति से बाकी फ़ंक्शन की पठनीयता क्यों प्रभावित होनी चाहिए?

एक विधि से कई बार लौटने के बारे में नकारात्मक के रूप में - डिबगर अब बहुत शक्तिशाली हैं, और यह पता लगाना बहुत आसान है कि किसी विशेष फ़ंक्शन को कहाँ और कब वापस कर रहे हैं।

एक फ़ंक्शन में कई रिटर्न होने से रखरखाव प्रोग्रामर की नौकरी प्रभावित नहीं होगी।

गरीब कोड पठनीयता होगा।


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

10
मैं शुरुआती ब्रेस पर एक ब्रेकपॉइंट लगाऊंगा। यदि आप फ़ंक्शन के माध्यम से कदम बढ़ाते हैं, तो न केवल आप चेक को क्रम में प्रदर्शन करते हुए देख सकते हैं, लेकिन यह बहुतायत से स्पष्ट होगा जो उस रन के लिए फ़ंक्शन को चेक करता है।
जॉन डुनगन

3
जॉन के साथ यहाँ सहमत हूँ ... मैं पूरे समारोह के माध्यम से कदम बढ़ाने में समस्या नहीं देखता।
नेलर

5
@ Nailer बहुत देर हो चुकी है, मैं विशेष रूप से सहमत हूं, यदि फ़ंक्शन के चरण के लिए बहुत बड़ा है, तो इसे कई फ़ंक्शन वैसे भी अलग किया जाना चाहिए!
Aidiakapi

1
जॉन से भी सहमत। हो सकता है कि यह एक पुराना स्कूल हो, जिसने ब्रेक प्वाइंट को सबसे नीचे रखा हो, लेकिन अगर आप उत्सुक हैं कि कोई फंक्शन क्या लौट रहा है, तो आप उक्त फंक्शन के माध्यम से कदम बढ़ाते हुए देखेंगे कि यह वैसे भी क्यों लौट रहा है। यदि आप केवल यह देखना चाहते हैं कि यह क्या रिटर्न देता है तो इसे अंतिम ब्रैकेट पर रखें।
user441521

70

जैसा कि अन्य लोगों ने उल्लेख किया है, एक प्रदर्शन हिट नहीं होना चाहिए, लेकिन अन्य विचार भी हैं। उन वैध चिंताओं के अलावा, यह आपको कुछ परिस्थितियों में गेटअप तक भी खोल सकता है। मान लीजिए कि आप एक doubleबदले के साथ काम कर रहे थे :

public void myfunction(double exampleParam){
    if(exampleParam > 0){
        //Body will *not* be executed if Double.IsNan(exampleParam)
    }
}

इसके विपरीत प्रतीत होता है कि विपरीत व्युत्क्रम के साथ:

public void myfunction(double exampleParam){
    if(exampleParam <= 0)
        return;
    //Body *will* be executed if Double.IsNan(exampleParam)
}

तो कुछ विशेष परिस्थितियों में जो प्रतीत होता है कि सही ढंग से उलटा ifहोना संभव नहीं है।


4
यह भी ध्यान दिया जाना चाहिए कि resharper quick fix inverts उदाहरणपराम> 0 से उदाहरणप्रेम <0 नहीं उदाहरणपराम <= 0 है जिसने मुझे पकड़ा है।
निक डे

1
और यही कारण है कि चेक को सामने होना चाहिए और साथ ही वापस लौटा दिया जाना चाहिए क्योंकि देव को लगता है कि एक नैनो को परिणाम चाहिए।
user441521

51

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

हालाँकि, अपवाद का समर्थन करने वाली भाषा में, यह ऐसी कोई गारंटी नहीं देता है। अपवादों का समर्थन करने वाली भाषा में, किसी भी कथन या अभिव्यक्ति का निष्पादन एक नियंत्रण प्रवाह का कारण बन सकता है जो विधि को समाप्त करता है। इसका मतलब है कि अंत में या कीवर्ड का उपयोग करके सफाई की जानी चाहिए।

वैसे भी, मैं कह रहा हूं कि मुझे लगता है कि बहुत से लोग 'केवल एक विधि के अंत में वापसी' को यह समझे बिना दिशा-निर्देश देते हैं कि ऐसा करना कभी क्यों अच्छी बात थी, और पठनीयता में सुधार के लिए नेस्टिंग को कम करना शायद एक बेहतर उद्देश्य है।


6
आपको अभी पता चला है कि अपवाद अग्लीएंडविल [tm] क्यों हैं?;; ;-) फैंसी, महंगे भेष में गोज़ हैं।
15 अक्टूबर को एरिकशोफर

16
@ यदि आप असाधारण खराब कोड के संपर्क में आ गए हैं। यह बहुत स्पष्ट है जब वे गलत तरीके से उपयोग किए जाते हैं, और वे आम तौर पर आपको उच्च-गुणवत्ता कोड लिखने की अनुमति देते हैं।
रॉबर्ट पॉलसन

यह वह उत्तर है जिसकी मुझे वास्तव में तलाश थी! यहां महान जवाब हैं, लेकिन उनमें से ज्यादातर उदाहरणों पर ध्यान केंद्रित करते हैं, न कि वास्तविक कारण पर कि यह सिफारिश क्यों पैदा हुई।
गुस्तावो मोरी

यह सबसे अच्छा जवाब है, क्योंकि यह बताता है कि यह पूरा तर्क पहले स्थान पर क्यों आया।
ब्यूटेल बटुक

If you've got deep nesting, maybe your function is trying to do too many things.=> यह आपके पिछले वाक्यांश से सही परिणाम नहीं है। क्योंकि इससे पहले कि आप कहें कि आप कोड A के साथ व्यवहार A को कोड D में बदल सकते हैं, कोड D के साथ A कोड क्लीनर है, प्रदान किया जाता है, लेकिन "बहुत सी चीजें" व्यवहार को संदर्भित करती हैं, जो परिवर्तित नहीं हुई हैं। इसलिए आपको इस निष्कर्ष से कोई मतलब नहीं है।
v.oddou

30

मैं जोड़ना चाहूंगा कि अगर गार्ड - क्लॉज है तो इसका उल्टा नाम है। मैं जब भी इसका उपयोग कर सकता हूं।

मुझे रीडिंग कोड से नफरत है जहां शुरुआत में है, तो कोड के दो स्क्रीन और कोई नहीं। बस अगर पलटना हो और वापस लौटना हो। इस तरह से कोई भी समय बर्बाद नहीं करेगा।

http://c2.com/cgi/wiki?GuardClause


2
बिल्कुल सही। यह एक परावर्तन का अधिक है। आपके द्वारा बताए गए अनुसार कोड को पढ़ना आसान है।
rpattabi

'वापसी' एक गार्ड क्लॉज के लिए पर्याप्त और भयानक नहीं है। मैं ऐसा करूंगा: अगर (पूर्व <= 0) WrongParamValueEx ("[MethodName] खराब इनपुट परम 1 मान {0} ... को फेंक दें और आपको अपवाद को पकड़ने और अपने ऐप लॉग में लिखने की आवश्यकता होगी
JohnJohnGalue

3
@ जॉन। यदि यह वास्तव में त्रुटि है तो आप सही हैं। लेकिन अक्सर यह नहीं है। और हर जगह पर किसी चीज़ की जाँच करने के बजाय, जहाँ विधि को विधि कहा जाता है, बस उसे जाँचता है और कुछ नहीं करता है।
पायोत्र पर्क

3
मैं एक ऐसी विधि को पढ़ना पसंद करूंगा जो कोड, अवधि, ifएस या न के दो स्क्रीन हो । lol
jpmc26

1
मैं व्यक्तिगत रूप से वास्तव में इस कारण के लिए शुरुआती रिटर्न पसंद करता हूं (यह भी: घोंसले के शिकार से बचना)। हालांकि, यह प्रदर्शन के बारे में मूल प्रश्न से संबंधित नहीं है और संभवतः एक टिप्पणी होनी चाहिए।
पैट्रिक एम

22

यह न केवल सौंदर्यशास्त्र को प्रभावित करता है, बल्कि यह कोड नेस्टिंग को भी रोकता है।

यह वास्तव में यह सुनिश्चित करने के लिए एक पूर्व शर्त के रूप में कार्य कर सकता है कि आपका डेटा भी मान्य है।


18

यह निश्चित रूप से व्यक्तिपरक है, लेकिन मुझे लगता है कि यह दो बिंदुओं पर दृढ़ता से सुधार करता है:

  • अब यह तुरंत स्पष्ट हो गया है कि आपके कार्य के पास रखने के लिए कुछ भी नहीं बचा है condition
  • यह नेस्टिंग स्तर को नीचे रखता है। घोंसले के शिकार से अधिक पठनीयता आपको लगता है कि होगा।

15

मल्टीपल रिटर्न पॉइंट्स C (और कुछ हद तक C ++) में एक समस्या थी, क्योंकि उन्होंने आपको प्रत्येक रिटर्न पॉइंट से पहले क्लीन-अप कोड को डुप्लिकेट करने के लिए मजबूर किया था। कचरा संग्रहण के साथ, try| finallyनिर्माण औरusing ब्लॉक, वास्तव में कोई कारण नहीं है कि आपको उनसे क्यों डरना चाहिए।

अंतत: यह आपके और आपके सहकर्मियों को पढ़ने में आसान लगता है।


वह एकमात्र कारण नहीं है। छद्म कोड को संदर्भित करने वाला एक अकादमिक कारण है जिसका सफाई के सामान जैसे व्यावहारिक विचारों से कोई लेना-देना नहीं है। इस अकादमिक कारण को अनिवार्य निर्माणों के मूल रूपों का सम्मान करना है। इसी तरह से आप लूप एक्साइटर्स को इसके बीच में न रखें। इस तरह, आक्रमणकारियों का कठोरता से पता लगाया जा सकता है और व्यवहार को सिद्ध किया जा सकता है। या समाप्ति को सिद्ध किया जा सकता है।
v.oddou

1
समाचार: वास्तव में मैंने एक बहुत ही व्यावहारिक कारण पाया है कि क्यों जल्दी ब्रेक और रिटर्न खराब हैं, और यह मैंने जो कहा, उसका सीधा विश्लेषण है। यहाँ आप जाते हैं, इंटेल C ++ कंपाइलर उपयोग-गाइड पेपर: d3f8ykwhia686p.cloudfront.net/1live/intel/… । मुख्य वाक्यांश:a loop that is not vectorizable due to a second data-dependent exit
v.oddou

12

प्रदर्शन-वार, दोनों दृष्टिकोणों के बीच कोई ध्यान देने योग्य अंतर नहीं होगा।

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

विचार के प्रतिस्पर्धी स्कूल हैं जिनके लिए दृष्टिकोण बेहतर है।

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

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


11

गार्ड क्लॉस या पूर्व-स्थितियां (जैसा कि आप शायद देख सकते हैं) यह देखने के लिए जांचें कि क्या कोई निश्चित शर्त पूरी हुई है और फिर कार्यक्रम का प्रवाह टूट गया है। वे उन स्थानों के लिए महान हैं जहाँ आप वास्तव में केवल एक ifबयान के एक परिणाम में रुचि रखते हैं । इसलिए कहने के बजाय:

if (something) {
    // a lot of indented code
}

आप स्थिति को उलटते हैं और तोड़ते हैं यदि वह उलट स्थिति पूरी हो जाती है

if (!something) return false; // or another value to show your other code the function did not execute

// all the code from before, save a lot of tabs

returnके रूप में गंदा के रूप में कहीं नहीं है goto। यह आपको अपने शेष कोड को दिखाने के लिए एक मान पारित करने की अनुमति देता है जो फ़ंक्शन नहीं चला सकता है।

आपको सबसे अच्छे उदाहरण दिखाई देंगे जहाँ इसे नेस्टेड स्थितियों में लागू किया जा सकता है:

if (something) {
    do-something();
    if (something-else) {
        do-another-thing();
    } else {
        do-something-else();
    }
}

बनाम:

if (!something) return;
do-something();

if (!something-else) return do-something-else();
do-another-thing();

आपको लगता है कि कुछ लोग बहस कर रहे हैं कि पहला क्लीनर है, लेकिन यह पूरी तरह से व्यक्तिपरक है। कुछ प्रोग्रामर यह जानना चाहते हैं कि इंडेंटेशन के तहत किन परिस्थितियों में कुछ चल रहा है, जबकि मैं विधि प्रवाह को रेखीय रखना चाहूंगा।

मैं एक पल के लिए यह नहीं बताऊँगा कि प्रीऑन्स आपके जीवन को बदल देगा या आपको बिछा देगा, लेकिन आपको अपना कोड बस पढ़ने में थोड़ा आसान लग सकता है।


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

@ ऑथरसाइड: पूरी तरह से सहमत हैं। सीरियल फैशन में लिखे गए रिटर्न आपके मस्तिष्क को संभावित रास्तों को क्रमबद्ध करने की आवश्यकता बनाते हैं। जब इफ-ट्री को सीधे कुछ प्रतिगामी तर्क में मैप किया जा सकता है, उदाहरण के लिए इसे लिस्प में संकलित किया जा सकता है। लेकिन धारावाहिक वापसी के लिए एक कंपाइलर को बहुत कठिन काम करना होगा। यहाँ बिंदु का स्पष्ट रूप से इस काल्पनिक परिदृश्य से कोई लेना-देना नहीं है, इसका कोड विश्लेषण, अनुकूलन की संभावना, सुधार के प्रमाण, समाप्ति के प्रमाण और अपरिवर्तनवाद का पता लगाने के साथ करना है।
v.oddou

1
किसी भी तरह से किसी को अधिक घोंसले के साथ कोड पढ़ना आसान नहीं लगता है। अधिक ब्लॉक की तरह कुछ एक नज़र में पढ़ने के लिए आसान है। यहाँ कोई रास्ता नहीं है पहला उदाहरण मुझे पढ़ने में आसान है और यह केवल 2 घोंसले में जाता है जब वास्तविक दुनिया में इस तरह का अधिकांश कोड गहरा हो जाता है और प्रत्येक घोंसले के साथ इसे पालन करने के लिए अधिक मस्तिष्क शक्ति की आवश्यकता होती है।
user441521

1
यदि घोंसला दो बार गहरा था, तो आपको इस सवाल का जवाब देने में कितना समय लगेगा: "जब आप 'कुछ-कुछ' करते हैं?" मुझे लगता है कि आप इसे बिना कलम और कागज के जवाब देने के लिए कठोर होंगे। लेकिन आप बहुत आसानी से इसका जवाब दे सकते हैं अगर यह सभी गार्ड-क्लॉज़ेड था।
डेविड स्टोफर 29'18

9

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


8

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

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


7

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

जैसे।

 bool PerformDefaultOperation()
 {
      bool succeeded = false;

      DataStructure defaultParameters;
      if ((defaultParameters = this.GetApplicationDefaults()) != null)
      {
           succeeded = this.DoSomething(defaultParameters);
      }

      return succeeded;
 }

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


4
बूल PerformDefaultOperation () {DataStructure defaultParameters = this.GetApplicationDefaults (); वापसी (defaultParameters! = NULL && this.DoSomething (defaultParameters);} वहां;) ने इसे आपके लिए ठीक किया। :)
tchen

1
यह उदाहरण बहुत बुनियादी है और इस पैटर्न की बात याद आती है। इस फ़ंक्शन के बारे में 4 अन्य जांचें करें और फिर हमें बताएं कि यह अधिक पठनीय है, क्योंकि यह नहीं है।
user441521

5

कोड कैसे दिखता है, इसके बारे में कई अच्छे कारण हैं । लेकिन नतीजों का क्या ?

आइए कुछ C # कोड और उसके IL संकलित फ़ॉर्म पर एक नज़र डालें:


using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length == 0) return;
        if ((args.Length+2)/3 == 5) return;
        Console.WriteLine("hey!!!");
    }
}

यह सरल स्निपेट संकलित किया जा सकता है। आप ildasm के साथ उत्पन्न .exe फ़ाइल खोल सकते हैं और जांच सकते हैं कि परिणाम क्या है। मैं सभी कोडांतरक चीज़ पोस्ट नहीं करूँगा लेकिन मैं परिणामों का वर्णन करूँगा।

उत्पन्न IL कोड निम्नलिखित करता है:

  1. यदि पहली स्थिति झूठी है, तो कोड के लिए कूदता है जहां दूसरा है।
  2. यदि यह अंतिम निर्देश के लिए सही है। (नोट: अंतिम निर्देश एक वापसी है)।
  3. दूसरी स्थिति में परिणाम की गणना के बाद भी ऐसा ही होता है। तुलना करें और: कंसोल के लिए मिला। अगर यह सच है तो झूठ या अंत में हो।
  4. संदेश प्रिंट करें और वापस लौटें।

तो ऐसा लगता है कि कोड अंत तक कूद जाएगा। क्या होगा अगर हम नेस्टेड कोड के साथ एक सामान्य करते हैं?

using System;

public class Test {
    public static void Main(string[] args) {
        if (args.Length != 0 && (args.Length+2)/3 != 5) 
        {
            Console.WriteLine("hey!!!");
        }
    }
}

आईएल निर्देशों में परिणाम काफी समान हैं। अंतर यह है कि इससे पहले कि प्रति शर्त के लिए कूद रहे थे: यदि झूठे कोड के अगले टुकड़े में जाते हैं, अगर सच है तो अंत में जाएं। और अब IL कोड बेहतर तरीके से प्रवाहित होता है और इसमें 3 जंप होते हैं (कंपाइलर ने इसे थोड़ा अनुकूलित किया): 1. पहला जंप: जब लंबाई 0 से एक हिस्से तक होती है जहां कोड फिर से (थर्ड जंप) अंत में कूदता है। 2. दूसरा: एक निर्देश से बचने के लिए दूसरी स्थिति के बीच में। 3. तीसरा: यदि दूसरी स्थिति झूठी है, तो अंत में कूदो।

वैसे भी, कार्यक्रम काउंटर हमेशा कूद जाएगा।


5
यह अच्छी जानकारी है - लेकिन व्यक्तिगत रूप से मैं कम देखभाल नहीं कर सकता यदि IL "बेहतर तरीके से बहता है" क्योंकि जो लोग कोड का प्रबंधन करने वाले हैं वे किसी भी IL को देखने वाले नहीं हैं
JohnIdol

1
आप वास्तव में यह अनुमान नहीं लगा सकते हैं कि C # कंपाइलर के अगले अपडेट में ऑप्टिमाइज़ेशन पास कैसे होगा जो आप आज देखते हैं। व्यक्तिगत रूप से, मैं किसी भी समय IL में एक अलग परिणाम उत्पन्न करने के लिए C # कोड को ट्विक करने की कोशिश नहीं करूंगा, जब तक कि tesing से पता चलता है कि आपको कहीं न कहीं किसी तंग पाश में एक प्रदर्शन की समस्या है और आप अन्य विचारों से बाहर हैं । फिर भी, मैं अन्य विकल्पों के बारे में कठिन सोचूंगा। उसी नस में, मुझे संदेह है कि आपके पास कोई विचार है कि जेआईटी संकलक किस प्रकार के अनुकूलन कर रहा है आईएल के साथ जो प्रत्येक मंच के लिए इसे खिलाया जाता है ...
क्रेग

5

सिद्धांत में, inverting ifबेहतर प्रदर्शन का नेतृत्व कर सकता है अगर यह शाखा भविष्यवाणी हिट दर बढ़ाता है। व्यवहार में, मुझे लगता है कि यह जानना बहुत कठिन है कि शाखा भविष्यवाणी कैसे व्यवहार करेगी, विशेष रूप से संकलन के बाद, इसलिए मैं इसे अपने दिन-प्रतिदिन के विकास में नहीं करूंगा, सिवाय इसके कि मैं विधानसभा कोड लिख रहा हूं।

शाखा भविष्यवाणी के बारे में अधिक यहाँ


4

वह बस विवादास्पद है। जल्दी वापसी के सवाल पर "प्रोग्रामरों के बीच कोई समझौता" नहीं है। यह हमेशा व्यक्तिपरक है, जहां तक ​​मुझे पता है।

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

मुझे नहीं लगता कि आपको इस प्रश्न का कोई निर्णायक उत्तर मिलेगा।


मुझे आपके प्रदर्शन तर्क की समझ नहीं है। स्थितियां बूलियन हैं इसलिए परिणाम जो भी हो, हमेशा दो परिणाम होते हैं ... मुझे नहीं लगता कि एक बयान (या नहीं) को उलटने से परिणाम क्यों बदलेगा। (जब तक आप यह नहीं कह रहे हों कि हालत में "नहीं" शामिल है, प्रसंस्करण की एक औसत दर्जे की मात्रा को जोड़ता है ...
ओली

2
अनुकूलन इस तरह से काम करता है: यदि आप यह सुनिश्चित करते हैं कि सबसे आम मामला अन्य ब्लॉक के बजाय यदि ब्लॉक में है, तो सीपीयू में आमतौर पर पहले से ही बयान होगा यदि इसकी पाइप लाइन में लोड किया गया है। यदि स्थिति झूठी हो जाती है, तो सीपीयू को अपनी पाइपलाइन को खाली करने की जरूरत है और ...
दूसरों को

मैं यह भी करना पसंद करता हूं कि अन्य लोग केवल पठनीयता के लिए क्या कह रहे हैं, मुझे "फिर" के बजाय "तब" ब्लॉक में नकारात्मक मामलों से नफरत है। सीपीयू क्या कर रहा है, यह जानने या विचार किए बिना भी यह समझ में आता है
एंड्रयू बुलॉक

3

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

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

गार्ड क्लॉज के साथ नेस्टिंग की जगह पर, मैंने क्लीन-कोड के एक हिस्से के बारे में भी गलत धारणा बनाई, जो अंत के बजाय फ़ंक्शन के भीतर बहुत पहले घटित होना चाहिए था। नेस्टेड शाखाओं के साथ, मैंने ऐसा कभी नहीं देखा होगा, आप यहां तक ​​कह सकते हैं कि उन्होंने मेरी गलत धारणा को जन्म दिया।

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


3

कई निकास बिंदुओं से बचना कर सकते हैं निष्पादन लाभ के लिए सीसा। मुझे C # के बारे में निश्चित नहीं है, लेकिन C ++ में नामांकित रिटर्न वैल्यू ऑप्टिमाइजेशन (कॉपी एलिसन, आईएसओ सी ++ '03 12.8 / 15) एकल निकास बिंदु होने पर निर्भर करता है। यह ऑप्टिमाइज़ेशन आपके रिटर्न वैल्यू को कॉपी करने से बचता है (आपके विशिष्ट उदाहरण में इससे कोई फर्क नहीं पड़ता)। यह तंग छोरों के प्रदर्शन में काफी लाभ प्राप्त कर सकता है, क्योंकि जब आप एक बार एक निर्माणकर्ता और एक विध्वंसक को बचा रहे होते हैं, तो फ़ंक्शन को लागू किया जाता है।

लेकिन अतिरिक्त निर्माणकर्ता और विध्वंसक कॉल को बचाने के 99% मामलों के लिए पठनीयता नेस्टेड ifब्लॉक परिचय के नुकसान के लायक नहीं है (जैसा कि अन्य ने बताया है)।


2
वहाँ। यह मुझे 3 लंबे सवालों के जवाब में एक ही चीज़ (जिसमें से 2 जमे हुए किया जा रहा है) में से दसियों जवाब के लंबे समय तक ले लिया, अंत में NRVO का उल्लेख किसी को खोजने के लिए। जी ... धन्यवाद।
v.oddou

2

यह राय की बात है।

मेरा सामान्य तरीका यह होगा कि मैं एक लाइन इफ़्स से बचूँ, और एक विधि के बीच में लौटूँ।

आप अपनी पद्धति में हर जगह यह चाहते हैं कि लाइनें न हों, लेकिन आपकी पद्धति के शीर्ष पर मान्यताओं का एक गुच्छा जाँचने के लिए कुछ कहा जाना चाहिए, और यदि वे सभी पास होते हैं तो केवल अपना वास्तविक काम कर रहे हैं।


2

मेरी राय में यदि आप केवल शून्य (या कुछ बेकार रिटर्न कोड जिसे आप कभी भी चेक नहीं करते हैं) वापस ला रहे हैं तो यह जल्दी ठीक होता है और यह पठनीयता में सुधार कर सकता है क्योंकि आप घोंसले के शिकार से बचते हैं और उसी समय आप स्पष्ट कर देते हैं कि आपका कार्य हो चुका है।

यदि आप वास्तव में एक रिटर्नवैल्यू लौटा रहे हैं - घोंसला बनाना आमतौर पर एक बेहतर तरीका है जिससे आप अपने रिटर्नवैल्यू को सिर्फ एक ही स्थान पर (अंत में - डुह) लौटा सकते हैं, और यह आपके कोड को कई मामलों में अधिक बनाए रख सकता है।


1

मुझे यकीन नहीं है, लेकिन मुझे लगता है, कि आर # दूर कूद से बचने की कोशिश करता है। जब आपके पास IF-ELSE हो, तो कंपाइलर कुछ ऐसा करता है:

स्थिति झूठी -> झूठी_कॉन्डीशन_लैब पर दूर तक कूदें

true_condition_label: अनुदेश 1 ... अनुदेश_एन

false_condition_label: अनुदेश 1 ... अनुदेश_एन

अंत ब्लॉक

यदि स्थिति सही है तो कोई जम्प और कोई रोलआउट L1 कैश नहीं है, लेकिन false_condition_label पर कूदना बहुत दूर हो सकता है और प्रोसेसर को अपना कैश रोलआउट करना होगा। कैश को सिंक्रोनाइज़ करना महंगा है। आर # दूर की छलांग को छोटी छलांग में बदलने की कोशिश करता है और इस मामले में बड़ी संभावना है, कि सभी निर्देश पहले से ही कैश में हैं।


0

मुझे लगता है कि यह निर्भर करता है कि आप क्या पसंद करते हैं, जैसा कि उल्लेख किया गया है, कोई सामान्य समझौता नहीं है। झुंझलाहट को कम करने के लिए, आप "संकेत" के लिए इस तरह की चेतावनी को कम कर सकते हैं


0

मेरा विचार है कि किसी फ़ंक्शन के बीच में "वापसी" इतनी "व्यक्तिपरक" नहीं होनी चाहिए। कारण काफी सरल है, इस कोड को लें:

    फ़ंक्शन do_something (डेटा) {

      अगर (is_valid_data (डेटा)) 
            विवरण झूठा है;


       do_something_that_take_an_hour (डेटा);

       istance = new object_with_very_painful_constructor (डेटा);

          अगर (istance मान्य नहीं है) {
               त्रुटि संदेश( );
                वापसी ;

          }
       connect_to_database ();
       get_some_other_data ();
       वापसी;
    }

हो सकता है कि पहले "वापसी" यह SO सहज नहीं है, लेकिन यह वास्तव में बचत है। स्वच्छ कोड के बारे में बहुत सारे "विचार" हैं, बस अपने "व्यक्तिपरक" बुरे विचारों को खोने के लिए अधिक अभ्यास की आवश्यकता है।


एक अपवाद भाषा के साथ आपके पास ये समस्याएँ नहीं हैं।
user441521

0

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


गंभीर जवाब को पढ़ना, ऐसा लगता है जैसे नेस्टेड कोड को कंपाइलर द्वारा इस तरह से अनुकूलित किया गया था कि निष्पादित कोड कई रिटर्न का उपयोग करने से तेज है। हालांकि, भले ही कई रिटर्न तेज थे, यह अनुकूलन शायद आपके आवेदन को इतना तेज नहीं करेगा ... :)
हैंगिंग

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