यदि उनके पास आंतरिक संरचना है तो क्या लंबे कार्य स्वीकार्य हैं?


23

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

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


5
मैं नियमित रूप से 400 + लाइन फ़ंक्शन / विधियाँ लिखता हूँ। होगा कि 500 ​​मामले स्विच स्टेटमेंट कहीं :) :)
कॉलम रोजर्स

7
@ कैलम रोजर्स: पायथन में, 500 केस स्विच स्टेटमेंट होने के बजाय, आप लुकअप डिक्शनरी / मैपिंग का उपयोग करेंगे। मेरा मानना ​​है कि वे 500 या तो स्विच-केस स्टेटमेंट से बेहतर हैं। आपको अभी भी शब्दकोश परिभाषा की 500 लाइनों की आवश्यकता है (वैकल्पिक रूप से, पायथन में, आप प्रतिबिंब का उपयोग गतिशील रूप से उन्हें एन्यूमरेट करने के लिए कर सकते हैं), लेकिन शब्दकोश परिभाषा डेटा है, कोड नहीं; और बड़े फ़ंक्शन परिभाषा की तुलना में बड़े डेटा परिभाषा होने के बारे में बहुत कम योग्यताएं हैं। इसके अतिरिक्त, डेटा कोड से अधिक मजबूत है।
रेयान

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

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

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

जवाबों:


21

हमेशा नियम को याद रखें, एक कार्य एक काम करता है और यह अच्छी तरह से करता है! यदि आप ऐसा कर सकते हैं, तो नेस्टेड कार्यों से बचें।

यह पठनीयता और परीक्षण में बाधा डालता है।


9
मैं हमेशा इस नियम से नफरत करता हूं क्योंकि एक ऐसा कार्य जो "एक चीज" करता है जब उच्च स्तर पर देखा जाता है तो निचले स्तर पर देखे जाने पर "कई चीजें" कर सकता है। यदि गलत तरीके से लागू किया जाता है, तो "एक बात" नियम अत्यधिक ठीक-ठाक एपीआई को जन्म दे सकता है।
dsimcha

1
@dsimcha: किस नियम का गलत इस्तेमाल नहीं किया जा सकता है और इससे समस्याएँ पैदा हो सकती हैं? जब कोई व्यक्ति "नियम" लागू कर रहा होता है, तो वे उस बिंदु से अतीत होते हैं जहां वे उपयोगी होते हैं, बस एक और बाहर लाएं: सभी सामान्यीकरण झूठे हैं।

2
@Roger: एक वैध शिकायत, सिवाय इसके कि IMHO नियम को अक्सर अत्यधिक बारीक, अतिरंजित एपीआई को सही ठहराने के लिए गलत व्यवहार किया जाता है। इसमें ठोस लागत है कि एपीआई सरल, सामान्य उपयोग के मामलों के लिए उपयोग करने के लिए कठिन और अधिक क्रिया हो जाता है।
dsimcha

2
"हाँ, नियम गलत है, लेकिन नियम बहुत अधिक गलत है!" : पी

1
मेरा कार्य एक काम करता है और यह उस चीज को बहुत अच्छी तरह से करता है: अर्थात्, 27 स्क्रीनफुल पर कब्जा। :)
काज

12

कुछ लोगों ने तर्क दिया है कि छोटे कार्य लंबे कार्यों की तुलना में अधिक त्रुटि वाले हो सकते हैं

कार्ड और ग्लास (1990) बताते हैं कि डिजाइन की जटिलता में वास्तव में दो पहलू शामिल हैं: प्रत्येक घटक के भीतर जटिलता और घटकों के साथ संबंधों की जटिलता।

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

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


क्या आपने "क्लीन कोड" पढ़ा है? यह इसकी चर्चा करता है। amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/…
मार्टिन विकमन

9

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

मुझे पता है कि जैसे ही मैं पेज अप / डाउन को पुश करता हूं या कोड के एक अलग सेक्शन में जाता हूं, मैं पिछले पृष्ठ से केवल 7 +/- 2 चीजें याद रख सकता हूं। और दुर्भाग्य से, नए कोड को पढ़ते समय उन स्थानों में से कुछ का उपयोग किया जा रहा है।

मैं हमेशा कंप्यूटर की रजिस्टरों (CISC, RISC नहीं) की तरह अपनी अल्पकालिक स्मृति के बारे में सोचना पसंद करता हूं। यदि आपके पास एक ही पृष्ठ पर संपूर्ण फ़ंक्शन है, तो आप प्रोग्राम के किसी अन्य अनुभाग से आवश्यक जानकारी प्राप्त करने के लिए कैश पर जा सकते हैं। यदि पूरा फ़ंक्शन किसी पेज पर फिट नहीं हो सकता है, तो यह हमेशा हर ऑपरेशन के बाद किसी भी मेमोरी को डिस्क पर धकेलने के बराबर होगा।


3
आपको बस इसे एक मैट्रिक्स प्रिंटर पर प्रिंट करने की आवश्यकता है - देखें, एक ही बार में 10 मीटर का कोड :)

या उच्च रिज़ॉल्यूशन वाला एक मॉनिटर प्राप्त करें
frogstarr78

और ऊर्ध्वाधर अभिविन्यास में इसका उपयोग करें।
15

4

सामान्य बाहरी कार्यों के बजाय नेस्टेड फ़ंक्शन का उपयोग क्यों करें?

यहां तक ​​कि अगर बाहरी कार्यों को केवल आपके एक, पूर्व-बड़े फ़ंक्शन में उपयोग किया जाता है, तो भी यह पूरी तरह से पढ़ने में आसान बनाता है:

DoThing(int x){
    x += ;1
    int y = FirstThing(x);
    x = SecondThing(x, y);
    while(spoo > fleem){
        x = ThirdThing(x+y);
    }
}

FirstThing(int x){...}
SecondThing(int x, int y){...}
ThirdThing(int z){...}

2
दो संभावित कारण: नेमस्पेस को अव्यवस्थित करना, और मैं "लेक्सिकल निकटता" कहूँगा। ये आपकी भाषा के आधार पर अच्छे या बुरे कारण हो सकते हैं।
फ्रैंक शीयर

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

3

मेरे पास इस समय (उद्धरण देने के लिए) मेरे सामने पुस्तक नहीं है, लेकिन कोड के अनुसार फ़ंक्शन की लंबाई के लिए "मिठास" को उनके शोध के अनुसार कोड की 25-50 पंक्तियों के आसपास था।

ऐसे समय होते हैं जहां लंबे समय तक कार्य करना ठीक होता है:

  • जब फ़ंक्शन की चक्रीय जटिलता कम होती है। आपके साथी डेवलपर्स थोड़ा निराश हो सकते हैं यदि उन्हें एक फ़ंक्शन को देखना है जिसमें एक विशाल अगर कथन है और उसके लिए दूसरा विवरण है यदि एक ही समय में स्क्रीन पर नहीं है।

ऐसे समय जहां लंबे कार्य करना ठीक नहीं है:

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

लब्बोलुआब यह है कि स्थिरता आपकी सूची में सर्वोच्च प्राथमिकताओं में से एक होनी चाहिए। यदि कोई अन्य देव आपके कोड को नहीं देख सकता है और 5 सेकंड से कम समय में कोड क्या कर रहा है, इसका "जिस्ट" प्राप्त कर सकता है, तो यह बताने के लिए उर कोड पर्याप्त "मेटाडेटा" प्रदान नहीं करता है। अन्य देवता यह बताने में सक्षम होंगे कि आपकी कक्षा कोड की 100+ पंक्तियों को पढ़ने के बजाय आपके चुने हुए IDE में ऑब्जेक्ट ब्राउज़र को देखकर क्या कर रही है।

छोटे कार्यों के निम्नलिखित फायदे हैं:

  • पोर्टेबिलिटी: कार्यक्षमता को स्थानांतरित करना बहुत आसान है (या तो वर्ग के भीतर एक अलग करने के लिए रिफैक्टिंग पर)
  • डिबगिंग: जब आप स्टैकट्रेस को देखते हैं तो एक त्रुटि को इंगित करने के लिए यह बहुत तेज़ी से होता है यदि आप 100 के बजाय 25 लाइनों के कोड के साथ एक फ़ंक्शन को देख रहे हैं।
  • पठनीयता - फ़ंक्शन का नाम बताता है कि कोड का एक पूरा ब्लॉक क्या कर रहा है। यदि वे इसके साथ काम नहीं कर रहे हैं, तो आपकी टीम का एक देवता उस ब्लॉक को पढ़ना नहीं चाहेगा। साथ ही, अधिकांश आधुनिक आईडीई के दूसरे देव में बेहतर समझ हो सकती है कि ऑब्जेक्ट ब्राउज़र में फ़ंक्शन नामों को पढ़कर आपकी कक्षा क्या कर रही है।
  • नेविगेशन - अधिकांश आईडीई आपको कार्यों के नाम पर खोज करने देगा। इसके अलावा, अधिकांश आधुनिक आईडीई में किसी अन्य विंडो में किसी फ़ंक्शन के स्रोत को देखने की क्षमता होती है, इससे अन्य देवों को स्क्रॉल करने के बजाय 2 स्क्रीन (यदि मल्टी-मॉनिटर) पर आपके लंबे फ़ंक्शन को देखने की सुविधा मिलती है।

सूची चलती जाती है.....


2

इसका उत्तर यह निर्भर करता है, हालांकि आपको संभवतः इसे एक कक्षा में बदलना चाहिए।


जब तक आप एक ओओपीएल में नहीं लिख रहे हैं, जिस स्थिति में शायद नहीं है :)
फ्रैंक

dsimcha ने उल्लेख किया कि वे D और पायथन का उपयोग कर रहे थे।
frogstarr78

क्लास बहुत बार उन लोगों के लिए बैसाखी होती है, जिन्होंने लेक्सिकल क्लोजर जैसी चीजों के बारे में कभी नहीं सुना है।
काज

2

मुझे अधिकांश नेस्टेड फ़ंक्शन पसंद नहीं हैं। लैम्ब्डा उस श्रेणी में आते हैं, लेकिन आमतौर पर मुझे ध्वज नहीं दिखाते हैं जब तक कि उनके पास 30-40 से अधिक वर्ण न हों।

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

मैं मानता हूं कि एक फंक्शन को डू इट थिंग चाहिए। अन्य कार्य करना अन्य कार्य करते हैं। इसलिए यदि आपके पास 200-लाइन फ़ंक्शन डूइंग इट थिंग है, और यह सब बहता है, तो यह ए-ओके है।


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

1

क्या यह स्वीकार्य है? यह वास्तव में एक सवाल है जिसका आप केवल उत्तर दे सकते हैं। क्या फ़ंक्शन प्राप्त करता है जिसे इसकी आवश्यकता है? क्या यह बनाए रखने योग्य है? क्या यह आपकी टीम के अन्य सदस्यों के लिए 'स्वीकार्य' है? यदि ऐसा है, तो वास्तव में यही मायने रखता है।

संपादित करें: मैंने नेस्टेड फ़ंक्शंस के बारे में बात नहीं देखी। व्यक्तिगत रूप से, मैं उनका उपयोग नहीं करूंगा। मैं इसके बजाय नियमित कार्यों का उपयोग करेगा।


1

एक लंबी "सीधी-रेखा" फ़ंक्शन चरणों का एक लंबा अनुक्रम निर्दिष्ट करने का एक स्पष्ट तरीका हो सकता है जो हमेशा किसी विशेष अनुक्रम में होता है।

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

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

कुछ भाषाओं में, यह समस्या आसानी से टल जाती है: कोड के एक स्थानीय खंड को अपने स्वयं के ब्लॉक में लपेटा जा सकता है, जैसे कि "{...}", जिसके भीतर कोई भी नव-प्रचलित चर केवल उस ब्लॉक में दिखाई देते हैं। कुछ भाषाओं, जैसे कि पायथन, में इस विशेषता का अभाव है, इस स्थिति में स्थानीय कार्य छोटे दायरे वाले क्षेत्रों को लागू करने के लिए उपयोगी हो सकते हैं।


1

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


0

जब मैं अजगर में प्रोग्रामिंग कर रहा होता हूं, तो मैं एक फ़ंक्शन लिखने के बाद वापस कदम रखना पसंद करता हूं और खुद से पूछता हूं कि क्या यह "ज़ेन ऑफ पायथन" का पालन करता है (अपने अजगर दुभाषिया में 'इस आयात करें'):

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


1
यदि स्पष्ट रूप से निहित से बेहतर है, तो हमें मशीन भाषा में कोड करना चाहिए। असेंबलर में भी नहीं; यह स्वचालित रूप से निर्देशों के साथ शाखा विलंब स्लॉट को भरने, रिश्तेदार शाखा ऑफसेट की गणना, और ग्लोबल्स के बीच प्रतीकात्मक संदर्भों को हल करने जैसी गंदा निहितार्थ करता है। यार, ये गूंगे अजगर यूजर्स और उनका छोटा सा धर्म ...
Kaz

0

उन्हें एक अलग मॉड्यूल में रखें।

मान लें कि आपके समाधान को ज़रूरत से ज़्यादा फूला हुआ नहीं है, तो आपके पास बहुत सारे विकल्प नहीं हैं। आप पहले से ही विभिन्न उप कार्यों में कार्यों को विभाजित कर चुके हैं, इसलिए सवाल यह है कि आपको इसे कहां रखना चाहिए:

  1. उन्हें केवल एक "सार्वजनिक" फ़ंक्शन वाले मॉड्यूल में रखें।
  2. उन्हें केवल "सार्वजनिक" (स्थिर) फ़ंक्शन के साथ कक्षा में रखें।
  3. एक फ़ंक्शन में उन्हें नेस्ट (जैसा कि आपने वर्णित किया है)।

अब दूसरा और तीसरा विकल्प दोनों कुछ अर्थों में घोंसले के शिकार हैं, लेकिन दूसरा विकल्प कुछ प्रोग्रामर को बुरा नहीं लगेगा। यदि आप दूसरे विकल्प से इंकार नहीं करते हैं तो मुझे तीसरे पर शासन करने का बहुत अधिक कारण नहीं दिखता है।

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