क्या ऐसे अच्छे कारण हैं कि किसी फ़ंक्शन में केवल एक रिटर्न स्टेटमेंट रखना बेहतर अभ्यास है?
या यह एक समारोह से लौटने के लिए ठीक है जैसे ही यह तार्किक रूप से ऐसा करने के लिए सही है, जिसका अर्थ है कि फ़ंक्शन में कई रिटर्न स्टेटमेंट हो सकते हैं?
क्या ऐसे अच्छे कारण हैं कि किसी फ़ंक्शन में केवल एक रिटर्न स्टेटमेंट रखना बेहतर अभ्यास है?
या यह एक समारोह से लौटने के लिए ठीक है जैसे ही यह तार्किक रूप से ऐसा करने के लिए सही है, जिसका अर्थ है कि फ़ंक्शन में कई रिटर्न स्टेटमेंट हो सकते हैं?
जवाबों:
मेरे पास अक्सर "आसान" स्थितियों के लिए लौटने के लिए एक विधि की शुरुआत में कई कथन हैं। उदाहरण के लिए, यह:
public void DoStuff(Foo foo)
{
if (foo != null)
{
...
}
}
... इस तरह अधिक पठनीय (IMHO) बनाया जा सकता है:
public void DoStuff(Foo foo)
{
if (foo == null) return;
...
}
तो हाँ, मुझे लगता है कि एक फ़ंक्शन / विधि से कई "निकास बिंदु" होना ठीक है।
DoStuff() { DoStuffInner(); IncreaseStuffCallCounter(); }
किसी ने कोड कम्प्लीट का उल्लेख या उद्धृत नहीं किया है इसलिए मैं इसे करूँगा।
प्रत्येक दिनचर्या में रिटर्न की संख्या कम से कम करें । एक दिनचर्या को समझना कठिन है अगर, इसे नीचे पढ़ रहे हैं, तो आप इस संभावना से अनजान हैं कि यह कहीं ऊपर लौट आया है।
पठनीयता को बढ़ाता है जब एक वापसी का उपयोग करें । निश्चित दिनचर्या में, एक बार जब आप उत्तर जान लेते हैं, तो आप इसे तुरंत कॉलिंग रूटीन में वापस करना चाहते हैं। यदि दिनचर्या को इस तरह से परिभाषित किया गया है कि उसे किसी भी सफाई की आवश्यकता नहीं है, तो तुरंत वापस नहीं आने का मतलब है कि आपको अधिक कोड लिखना होगा।
मैं कहूंगा कि कई निकास बिंदुओं के खिलाफ मनमाने ढंग से निर्णय लेना अविश्वसनीय रूप से नासमझी होगी क्योंकि मैंने तकनीक को बार-बार व्यवहार में उपयोगी पाया है , वास्तव में मैंने स्पष्टता के लिए कई बार मौजूदा निकास कोड को कई निकास बिंदुओं पर रिफैक्ट किया है। हम दो दृष्टिकोणों की तुलना इस प्रकार कर सकते हैं: -
string fooBar(string s, int? i) {
string ret = "";
if(!string.IsNullOrEmpty(s) && i != null) {
var res = someFunction(s, i);
bool passed = true;
foreach(var r in res) {
if(!r.Passed) {
passed = false;
break;
}
}
if(passed) {
// Rest of code...
}
}
return ret;
}
इसकी तुलना उस कोड से करें जहाँ कई निकास बिंदुओं की अनुमति है: -
string fooBar(string s, int? i) {
var ret = "";
if(string.IsNullOrEmpty(s) || i == null) return null;
var res = someFunction(s, i);
foreach(var r in res) {
if(!r.Passed) return null;
}
// Rest of code...
return ret;
}
मुझे लगता है कि उत्तरार्द्ध काफी स्पष्ट है। जहां तक मैं बता सकता हूं कि कई निकास बिंदुओं की आलोचना इन दिनों देखने का एक अधिक महत्वपूर्ण बिंदु है।
मैं वर्तमान में एक कोडबेस पर काम कर रहा हूं, जहां पर काम करने वाले दो लोग नेत्रहीन रूप से "सिंगल पॉइंट ऑफ एग्जिट" सिद्धांत की सदस्यता लेते हैं और मैं आपको बता सकता हूं कि अनुभव से, यह एक भयानक भयानक अभ्यास है। यह बनाए रखने के लिए कोड को बहुत कठिन बनाता है और मैं आपको दिखाता हूं कि क्यों।
"निकास के एकल बिंदु" सिद्धांत के साथ, आप अनिवार्य रूप से इस तरह दिखने वाले कोड के साथ हवा देते हैं:
function()
{
HRESULT error = S_OK;
if(SUCCEEDED(Operation1()))
{
if(SUCCEEDED(Operation2()))
{
if(SUCCEEDED(Operation3()))
{
if(SUCCEEDED(Operation4()))
{
}
else
{
error = OPERATION4FAILED;
}
}
else
{
error = OPERATION3FAILED;
}
}
else
{
error = OPERATION2FAILED;
}
}
else
{
error = OPERATION1FAILED;
}
return error;
}
न केवल यह कोड का पालन करना बहुत कठिन बनाता है, लेकिन अब बाद में कहें कि आपको वापस जाने और 1 और 2 के बीच एक ऑपरेशन जोड़ने की आवश्यकता है। आपको केवल पूरे फ्रिकिंग फ़ंक्शन के बारे में इंडेंट करना होगा, और सौभाग्य यह सुनिश्चित करेगा यदि आपकी / और स्थिति और ब्रेसिज़ ठीक से मेल खाते हैं।
यह विधि कोड रखरखाव को बहुत कठिन और त्रुटि प्रवण बनाती है।
संरचित प्रोग्रामिंग कहती है कि आपको प्रति फ़ंक्शन केवल एक बार विवरण देना चाहिए। इस जटिलता को सीमित करने के लिए है। मार्टिन फॉलर जैसे कई लोगों का तर्क है कि कई रिटर्न स्टेटमेंट के साथ फ़ंक्शन लिखना सरल है। वह इस तर्क को क्लासिक रिफैक्टिंग पुस्तक में लिखते हैं जो उन्होंने लिखा था। यह अच्छी तरह से काम करता है यदि आप उसकी अन्य सलाह का पालन करते हैं और छोटे कार्य लिखते हैं। मैं इस दृष्टिकोण से सहमत हूं और केवल सख्त संरचित प्रोग्रामिंग शुद्धतावादी प्रति फ़ंक्शन एकल रिटर्न स्टेटमेंट का पालन करते हैं।
GOTO
फ़ंक्शन के मौजूद होने पर भी नियंत्रण प्रवाह का उपयोग करने के बारे में बात की थी। यह कभी नहीं कहता है "कभी उपयोग न करें GOTO
"।
केंट बेक नोट के रूप में जब कार्यान्वयन पैटर्न में गार्ड क्लॉस पर चर्चा करते हुए एक रूटीन में एक ही प्रविष्टि और निकास बिंदु होता है ...
"एक ही दिनचर्या में कई स्थानों पर और बाहर कूदते समय भ्रम को रोकना संभव था। यह बहुत अच्छा समझ में आता है जब कई वैश्विक आंकड़ों के साथ लिखे गए फोरट्रान या असेंबली भाषा के कार्यक्रमों पर लागू किया जाता है, यहां तक कि यह भी समझना कि किन बयानों को निष्पादित किया गया था, कड़ी मेहनत थी। .. , छोटे तरीकों और ज्यादातर स्थानीय आंकड़ों के साथ, यह अनावश्यक रूप से रूढ़िवादी है। "
मुझे पता चलता है कि गार्ड के साथ लिखा गया एक फ़ंक्शन, if then else
बयानों के एक लंबे नेस्टेड गुच्छा का पालन करने के लिए बहुत आसान है ।
ऐसे फ़ंक्शन में जिसका कोई साइड-इफ़ेक्ट नहीं है, एक से अधिक रिटर्न होने का कोई अच्छा कारण नहीं है और आपको उन्हें एक कार्यात्मक शैली में लिखना चाहिए। साइड-इफेक्ट वाली विधि में, चीजें अधिक अनुक्रमिक (समय-अनुक्रमित) होती हैं, इसलिए आप एक अनिवार्य शैली में लिखते हैं, रिटर्न स्टेटमेंट को निष्पादित करने से रोकने के आदेश के रूप में उपयोग करते हैं।
दूसरे शब्दों में, जब संभव हो, इस शैली का पक्ष लें
return a > 0 ?
positively(a):
negatively(a);
इस पर
if (a > 0)
return positively(a);
else
return negatively(a);
यदि आप खुद को नेस्टेड स्थितियों की कई परतों को लिखते हुए पाते हैं, तो संभवत: एक ऐसा तरीका है जिससे आप उदाहरण के लिए विधेय सूची का उपयोग कर सकते हैं। यदि आप पाते हैं कि आपके ifs और elses वाक्यात्मक रूप से अलग हैं, तो आप इसे छोटे कार्यों में तोड़ सकते हैं। एक सशर्त ब्लॉक जो पाठ के एक स्क्रीनफुल से अधिक फैलता है, पढ़ना मुश्किल है।
हर भाषा पर लागू होने वाला कोई कठिन और तेज़ नियम नहीं है। सिंगल रिटर्न स्टेटमेंट होने जैसी कोई बात आपके कोड को अच्छा नहीं बनाएगी। लेकिन अच्छा कोड आपको इस तरह से अपने कार्यों को लिखने की अनुमति देगा।
मैंने इसे C ++ के लिए कोडिंग मानकों में देखा है जो C से हैंग-ओवर थे, जैसे अगर आपके पास RAII या अन्य स्वचालित मेमोरी प्रबंधन नहीं है, तो आपको प्रत्येक रिटर्न के लिए सफाई करनी होगी, जिसका मतलब है कि कट-एंड-पेस्ट क्लीन-अप या एक गोटो (तार्किक रूप से प्रबंधित भाषाओं में 'अंत में' के समान), दोनों को ही खराब रूप माना जाता है। यदि आपकी प्रथाओं में C ++ या किसी अन्य स्वचालित मेमोरी सिस्टम में स्मार्ट पॉइंटर्स और संग्रह का उपयोग करना है, तो इसके लिए एक मजबूत कारण नहीं है, और यह पठनीयता, और एक निर्णय कॉल के बारे में अधिक हो जाता है।
auto_ptr
, तो आप समानांतर में सादे पॉइंटर्स का उपयोग कर सकते हैं। हालाँकि पहले स्थान पर गैर-अनुकूलन वाले संकलक के साथ 'अनुकूलित' कोड लिखना अजीब होगा।
try
... finally
) और आपको संसाधन रखरखाव करने की आवश्यकता है जो आप एक के साथ कर सकते हैं एक विधि के अंत में लौटें। इससे पहले कि आप ऐसा करें, आपको स्थिति से छुटकारा पाने के लिए कोड को फिर से भरने पर गंभीरता से विचार करना चाहिए।
मैं इस विचार के लिए झुक गया हूं कि फ़ंक्शन के बीच में बयान वापस करना बुरा है। आप फ़ंक्शन के शीर्ष पर कुछ गार्ड क्लॉज़ बनाने के लिए रिटर्न का उपयोग कर सकते हैं, और निश्चित रूप से संकलक को बता सकते हैं कि बिना समस्या के फ़ंक्शन के अंत में क्या लौटना है, लेकिन फ़ंक्शन के बीच में रिटर्न को याद रखना आसान हो सकता है और कर सकते हैं व्याख्या करने के लिए फ़ंक्शन को कठिन बनाएं।
क्या ऐसे अच्छे कारण हैं कि किसी फ़ंक्शन में केवल एक रिटर्न स्टेटमेंट रखना बेहतर अभ्यास है?
हाँ , वहाँ हैं:
प्रश्न अक्सर कई रिटर्न के बीच एक गलत द्वंद्वात्मकता के रूप में प्रस्तुत किया जाता है या यदि बयान में गहराई से घोंसला होता है। लगभग हमेशा एक तीसरा समाधान होता है जो केवल एक ही निकास बिंदु के साथ बहुत रैखिक (कोई गहरी घोंसले का शिकार) नहीं है।
अद्यतन : जाहिर तौर पर MISRA दिशानिर्देश एकल निकास को भी बढ़ावा देते हैं।
स्पष्ट होने के लिए, मैं यह नहीं कह रहा हूं कि कई रिटर्न रखना हमेशा गलत है। लेकिन अन्यथा समकक्ष समाधान दिए गए हैं, एक ही रिटर्न के साथ एक को पसंद करने के बहुत सारे अच्छे कारण हैं।
Contract.Ensures
कई रिटर्न पॉइंट्स के साथ उपयोग कर सकते हैं ।
goto
कॉमन क्लीनअप कोड प्राप्त करने के लिए उपयोग कर रहे हैं , तो आपने संभवतः फ़ंक्शन को सरल कर दिया है, ताकि return
क्लीन-अप कोड के अंत में एकल हो। तो आप कह सकते हैं कि आपने समस्या को हल कर लिया है goto
, लेकिन मैं कहूंगा कि आपने इसे सरल बनाकर इसे हल कर दिया है return
।
एकल निकास बिंदु होने से डिबगिंग में एक लाभ मिलता है, क्योंकि यह आपको फ़ंक्शन के अंत में एक एकल ब्रेकपॉइंट सेट करने की अनुमति देता है यह देखने के लिए कि वास्तव में क्या मूल्य वापस आने वाला है।
सामान्य तौर पर मैं एक फंक्शन से केवल एक ही एग्जिट पॉइंट लेने की कोशिश करता हूं। हालांकि, कई बार ऐसा करने से वास्तव में एक अधिक जटिल फ़ंक्शन बॉडी बनाने की आवश्यकता होती है, जिस स्थिति में कई एक्ज़िट पॉइंट्स होना बेहतर होता है। परिणामी जटिलता के आधार पर वास्तव में यह एक "निर्णय कॉल" होना है, लेकिन लक्ष्य को जटिलता और समझ के त्याग के बिना यथासंभव कुछ निकास बिंदु होना चाहिए।
नहीं, क्योंकि हम 1970 के दशक में नहीं रहते हैं । यदि आपका कार्य लंबा है, तो कई रिटर्न एक समस्या है, यह बहुत लंबा है।
(इस तथ्य से अलग कि अपवाद वाली भाषा में किसी भी मल्टी-लाइन फ़ंक्शन के पास वैसे भी कई निकास बिंदु होंगे।)
मेरी प्राथमिकता एकल निकास के लिए होगी जब तक कि यह वास्तव में चीजों को जटिल न करे। मैंने पाया है कि कुछ मामलों में, कई मौजूद बिंदु अन्य महत्वपूर्ण डिजाइन समस्याओं का सामना कर सकते हैं:
public void DoStuff(Foo foo)
{
if (foo == null) return;
}
इस कोड को देखने पर, मैं तुरंत पूछूंगा:
इन सवालों के जवाबों के आधार पर ऐसा हो सकता है
उपरोक्त दोनों मामलों में यह सुनिश्चित करने के लिए कि शायद 'फू ’कभी अशक्त न हो और संबंधित कॉलर्स बदल गया हो, कोड को संभवतः एक जोर के साथ फिर से काम किया जा सकता है।
दो अन्य कारण हैं (विशिष्ट मुझे लगता है कि सी ++ कोड) जहां कई मौजूद हैं वास्तव में नकारात्मक प्रभाव पड़ सकता है। वे कोड आकार, और संकलक अनुकूलन हैं।
किसी गैर-पीओडी C ++ ऑब्जेक्ट को किसी फ़ंक्शन के बाहर निकलने के दायरे में इसके विध्वंसक कहा जाएगा। जहां कई रिटर्न स्टेटमेंट्स हैं, ऐसा हो सकता है कि स्कोप में अलग-अलग ऑब्जेक्ट्स हों और इसलिए कॉल करने के लिए डिस्ट्रक्टर्स की लिस्ट अलग होगी। इसलिए कंपाइलर को प्रत्येक रिटर्न स्टेटमेंट के लिए कोड जनरेट करना होगा:
void foo (int i, int j) {
A a;
if (i > 0) {
B b;
return ; // Call dtor for 'b' followed by 'a'
}
if (i == j) {
C c;
B b;
return ; // Call dtor for 'b', 'c' and then 'a'
}
return 'a' // Call dtor for 'a'
}
यदि कोड का आकार एक मुद्दा है - तो यह बचने लायक कुछ हो सकता है।
अन्य मुद्दा "नामांकित रिटर्न वैल्यू ऑप्टिमाइज़ेशन" (उर्फ कॉपी एलीशन, आईएसओ सी ++ +03 12.8 / 15) से संबंधित है। C ++ कॉपी कंस्ट्रक्टर को कॉल करने के लिए कार्यान्वयन को कार्यान्वित करने की अनुमति देता है यदि यह हो सकता है:
A foo () {
A a1;
// do something
return a1;
}
void bar () {
A a2 ( foo() );
}
कोड को बस के रूप में लेते हुए, 'a1' का निर्माण 'foo' में किया जाता है और फिर इसकी कॉपी निर्माण को 'a2' बनाने के लिए बुलाया जाएगा। हालाँकि, कॉपी एलीजन कंपाइलर को 'a2' के समान स्टैक पर 'a1' के निर्माण की अनुमति देता है। इसलिए फ़ंक्शन के वापस आने पर ऑब्जेक्ट को "कॉपी" करने की कोई आवश्यकता नहीं है।
मल्टीपल एग्जिट पॉइंट कंपाइलर के काम को जटिल बना देते हैं ताकि यह पता लगाने की कोशिश की जा सके और कम से कम वीसी ++ के अपेक्षाकृत हालिया संस्करण के लिए ऑप्टिमाइज़ेशन नहीं हुआ, जहां फ़ंक्शन बॉडी में कई रिटर्न थे। देखें विज़ुअल सी ++ 2005 में नामांकित वापसी मूल्य अनुकूलन अधिक जानकारी के लिए।
throw new ArgumentNullException()
इस मामले में C # में है) से असहमत हूं , मुझे वास्तव में आपके अन्य विचार पसंद हैं, वे सभी मेरे लिए मान्य हैं, और कुछ में महत्वपूर्ण हो सकते हैं आला संदर्भ।
foo
परीक्षण किया जा रहा है का सवाल विषय से कोई लेना-देना नहीं है, जो करना है if (foo == NULL) return; dowork;
याif (foo != NULL) { dowork; }
एकल निकास बिंदु होने से साइक्लोमैटिक जटिलता कम हो जाती है और इसलिए, सिद्धांत रूप में , इस संभावना को कम कर देता है कि जब आप इसे बदलते हैं तो आप अपने कोड में बगों को पेश करेंगे। हालाँकि, अभ्यास यह सुझाव देता है कि अधिक व्यावहारिक दृष्टिकोण की आवश्यकता है। इसलिए मेरा लक्ष्य एक एकल निकास बिंदु रखना है, लेकिन यदि अधिक पठनीय है, तो मेरे कोड को कई होने दें।
मैं खुद को केवल एक return
बयान का उपयोग करने के लिए मजबूर करता हूं , क्योंकि यह एक अर्थ में कोड गंध पैदा करेगा। मुझे समझाने दो:
function isCorrect($param1, $param2, $param3) {
$toret = false;
if ($param1 != $param2) {
if ($param1 == ($param3 * 2)) {
if ($param2 == ($param3 / 3)) {
$toret = true;
} else {
$error = 'Error 3';
}
} else {
$error = 'Error 2';
}
} else {
$error = 'Error 1';
}
return $toret;
}
(स्थितियां अर्हक हैं ...)
जितनी अधिक परिस्थितियां, जितना बड़ा कार्य होता है, पढ़ने में उतना ही कठिन होता है। इसलिए यदि आप कोड गंध से जुड़े हैं, तो आपको इसका एहसास होगा, और कोड को फिर से भरना होगा। दो संभावित समाधान हैं:
मल्टीपल रिटर्न
function isCorrect($param1, $param2, $param3) {
if ($param1 == $param2) { $error = 'Error 1'; return false; }
if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
return true;
}
अलग कार्य
function isEqual($param1, $param2) {
return $param1 == $param2;
}
function isDouble($param1, $param2) {
return $param1 == ($param2 * 2);
}
function isThird($param1, $param2) {
return $param1 == ($param2 / 3);
}
function isCorrect($param1, $param2, $param3) {
return !isEqual($param1, $param2)
&& isDouble($param1, $param3)
&& isThird($param2, $param3);
}
दी, यह लंबा और थोड़ा गड़बड़ है, लेकिन इस तरह से फ़ंक्शन को फिर से शुरू करने की प्रक्रिया में, हमने
मैं कहूंगा कि आपके पास आवश्यक रूप से कई होने चाहिए, या कोई भी जो कोड क्लीनर (जैसे गार्ड क्लॉज ) बनाता है ।
मैंने व्यक्तिगत रूप से किसी भी "सर्वोत्तम प्रथाओं" को कभी नहीं सुना / देखा है कि आप केवल एक रिटर्न स्टेटमेंट होना चाहिए।
अधिकांश भाग के लिए, मैं एक तर्क पथ के आधार पर जल्द से जल्द एक समारोह से बाहर निकलता हूं (गार्ड क्लॉज इस का एक उत्कृष्ट उदाहरण हैं)।
मेरा मानना है कि आमतौर पर कई रिटर्न अच्छे होते हैं (कोड में जो मैं सी # में लिखता हूं)। एकल-वापसी शैली सी से होल्डओवर है लेकिन आप शायद सी में कोडिंग नहीं कर रहे हैं।
सभी प्रोग्रामिंग भाषाओं में एक विधि के लिए केवल एक निकास बिंदु की आवश्यकता वाला कोई कानून नहीं है । कुछ लोग इस शैली की श्रेष्ठता पर जोर देते हैं, और कभी-कभी वे इसे "नियम" या "कानून" तक बढ़ाते हैं, लेकिन यह विश्वास किसी भी सबूत या शोध से समर्थित नहीं है।
C कोड में एक से अधिक रिटर्न स्टाइल एक बुरी आदत हो सकती है, जहाँ संसाधनों को स्पष्ट रूप से आबंटित किया जाना है, लेकिन जावा, C #, पायथन या जावास्क्रिप्ट जैसी भाषाएं जिनमें स्वत: कचरा संग्रह और try..finally
ब्लॉक ( using
C # में ब्लॉक) जैसे निर्माण हैं ), और यह तर्क लागू नहीं होता है - इन भाषाओं में, केंद्रीकृत मैनुअल संसाधन डीलोकेशन की आवश्यकता होना बहुत ही असामान्य है।
ऐसे मामले हैं जहां एक एकल वापसी अधिक पठनीय है, और ऐसे मामले जहां यह नहीं है। देखें कि क्या यह कोड की लाइनों की संख्या को कम करता है, तर्क को स्पष्ट करता है या ब्रेसिज़ और इंडेंट या अस्थायी चर की संख्या को कम करता है।
इसलिए, अपनी कलात्मक संवेदनाओं के अनुरूप अधिक से अधिक रिटर्न का उपयोग करें, क्योंकि यह एक लेआउट और पठनीयता का मुद्दा है, न कि तकनीकी।
मैंने इस बारे में अपने ब्लॉग पर अधिक लंबाई में बात की है ।
एकल निकास बिंदु के बारे में कहने के लिए अच्छी चीजें हैं, जैसे कि अपरिहार्य "तीर" प्रोग्रामिंग के बारे में कहने के लिए खराब चीजें हैं जो परिणाम देती हैं।
यदि इनपुट सत्यापन या संसाधन आवंटन के दौरान कई निकास बिंदुओं का उपयोग किया जाता है, तो मैं फ़ंक्शन के शीर्ष पर बहुत ही स्पष्ट रूप से सभी 'त्रुटि-निकास' को डालने का प्रयास करता हूं।
"एसएसडीएसएलपीडिया" के स्पार्टन प्रोग्रामिंग लेख और "पोर्टलैंड पैटर्न रिपॉजिटरीज़ विकी" के एकल फ़ंक्शन एक्ज़िट पॉइंट लेख दोनों के आस-पास कुछ व्यावहारिक तर्क हैं। इसके अलावा, निश्चित रूप से, इस पोस्ट पर विचार करना है।
यदि आप वास्तव में एक एकल स्थान में संसाधनों को जारी करने के लिए उदाहरण के लिए एक एकल निकास बिंदु (किसी भी गैर-अपवाद-सक्षम भाषा में) चाहते हैं, तो मुझे अच्छा होने के लिए गोटो का सावधानीपूर्वक आवेदन मिलता है; उदाहरण के लिए इसे देखें नहीं बल्कि उदाहरण के लिए (स्क्रीन रियल एस्टेट को बचाने के लिए संकुचित):
int f(int y) {
int value = -1;
void *data = NULL;
if (y < 0)
goto clean;
if ((data = malloc(123)) == NULL)
goto clean;
/* More code */
value = 1;
clean:
free(data);
return value;
}
व्यक्तिगत रूप से I, सामान्य रूप से, मैं एक से अधिक निकास-बिंदुओं को नापसंद करने से अधिक तीर प्रोग्रामिंग को नापसंद करता हूं, हालांकि दोनों सही ढंग से लागू होने पर उपयोगी होते हैं। सबसे अच्छा, निश्चित रूप से, न तो आवश्यकता के लिए अपने कार्यक्रम की संरचना करना है। अपने कार्य को कई विखंडू में तोड़कर आमतौर पर मदद करते हैं :)
हालांकि ऐसा करते समय, मुझे लगता है कि मैं इस उदाहरण में कई एक्ज़िट पॉइंट्स के साथ समाप्त होता हूं, जहां कुछ बड़े फ़ंक्शन को किसी भी छोटे हिस्से में तोड़ दिया गया है:
int g(int y) {
value = 0;
if ((value = g0(y, value)) == -1)
return -1;
if ((value = g1(y, value)) == -1)
return -1;
return g2(y, value);
}
परियोजना या कोडिंग दिशानिर्देशों के आधार पर, अधिकांश बॉयलर-प्लेट कोड को मैक्रोज़ द्वारा प्रतिस्थापित किया जा सकता है। एक साइड नोट के रूप में, इसे इस तरह से तोड़ने से फ़ंक्शन जी 0, जी 1, जी 2 को व्यक्तिगत रूप से परीक्षण करना बहुत आसान हो जाता है।
जाहिर है, एक OO और अपवाद-सक्षम भाषा में, मैं अगर-स्टेटमेंट्स का उपयोग नहीं करता (या बिल्कुल भी, अगर मैं इसे बहुत कम प्रयास के साथ दूर कर सकता था), और कोड बहुत अधिक सादा होगा। और गैर-तीर। और अधिकांश गैर-अंतिम रिटर्न शायद अपवाद होंगे।
संक्षेप में;
आप जानते हैं कि कहावत - सुंदरता देखने वाले की आंखों में है ।
कुछ लोग NetBeans और कुछ IntelliJ IDEA , कुछ पायथन द्वारा और कुछ PHP द्वारा शपथ लेते हैं ।
कुछ दुकानों में आप अपनी नौकरी खो सकते हैं यदि आप ऐसा करने पर जोर देते हैं:
public void hello()
{
if (....)
{
....
}
}
प्रश्न दृश्यता और स्थिरता के बारे में है।
मुझे तर्क और राज्य मशीनों के उपयोग को कम करने के लिए बूलियन बीजगणित का उपयोग करने का आदी है। हालांकि, ऐसे पिछले सहयोगी थे जो मानते थे कि कोडिंग में "गणितीय तकनीकों" का मेरा काम अनुपयुक्त है, क्योंकि यह दृश्यमान और अनुरक्षित नहीं होगा। और यह एक बुरा अभ्यास होगा। क्षमा करें लोग, मेरे द्वारा नियोजित तकनीक मेरे लिए बहुत दृश्यमान और रखरखाव योग्य है - क्योंकि जब मैं छह महीने बाद कोड में वापस आता हूं, तो मैं लौकिक स्पेगेटी की गड़बड़ी देखने के बजाय कोड को स्पष्ट रूप से समझूंगा।
अरे दोस्त (जैसे एक पूर्व ग्राहक कहा करते थे) वही करें जो आप चाहते हैं जब तक आप जानते हैं कि इसे कैसे ठीक किया जाए जब मुझे आपको इसे ठीक करने की आवश्यकता होती है।
मुझे याद है कि 20 साल पहले, मेरे एक सहयोगी को रोजगार के लिए निकाल दिया गया था, जिसे आज की फुर्तीली विकास रणनीति कहा जाएगा । उनके पास एक सावधानीपूर्वक वृद्धिशील योजना थी। लेकिन उसका प्रबंधक उस पर चिल्ला रहा था "आप उपयोगकर्ताओं के लिए आकस्मिक रूप से सुविधाएँ जारी नहीं कर सकते! आपको झरने से चिपकना चाहिए ।" प्रबंधक के प्रति उनकी प्रतिक्रिया थी कि वृद्धिशील विकास ग्राहक की जरूरतों के लिए अधिक सटीक होगा। वह ग्राहकों की जरूरतों के लिए विकसित होने में विश्वास करता था, लेकिन प्रबंधक "ग्राहक की आवश्यकता" को कोड करने में विश्वास करता था।
हम डेटा के सामान्यीकरण, एमवीपी और एमवीसी सीमाओं को तोड़ने के लिए अक्सर दोषी होते हैं । हम एक फ़ंक्शन का निर्माण करने के बजाय इनलाइन करते हैं। हम शॉर्टकट लेते हैं।
व्यक्तिगत रूप से, मेरा मानना है कि PHP बुरा अभ्यास है, लेकिन मुझे क्या पता है। सभी सैद्धांतिक तर्क नियमों के एक सेट को पूरा करने की कोशिश करते हैं
गुणवत्ता = परिशुद्धता, रखरखाव और लाभप्रदता।
अन्य सभी नियम पृष्ठभूमि में फीके हैं। और निश्चित रूप से यह नियम कभी नहीं मिटता:
आलस्य एक अच्छे प्रोग्रामर का गुण है।
मैं जल्दी लौटने के लिए गार्ड क्लॉस का उपयोग करने की ओर झुक गया और अन्यथा एक विधि के अंत में बाहर निकलता हूं। एकल प्रविष्टि और निकास नियम का ऐतिहासिक महत्व है और कई कोड (और कई दोष) के साथ एकल C ++ विधि के लिए 10 A4 पृष्ठों तक चलने वाली विरासत कोड से निपटने में विशेष रूप से सहायक था। हाल ही में, स्वीकार किए जाते हैं अच्छा अभ्यास तरीकों को छोटा रखने के लिए है जो कई निकास को समझने के लिए एक बाधा से कम करता है। ऊपर से कॉपी किए गए क्रोनोज़ उदाहरण में, सवाल यह है कि क्या होता है // बाकी कोड ... ??
void string fooBar(string s, int? i) {
if(string.IsNullOrEmpty(s) || i == null) return null;
var res = someFunction(s, i);
foreach(var r in res) {
if(!r.Passed) return null;
}
// Rest of code...
return ret;
}
मुझे लगता है कि उदाहरण कुछ हद तक वंचित है, लेकिन मैं एक LINQ बयान में फॉरेस्ट लूप को रिफैक्ट करने के लिए लुभाऊंगा, जिसे तब एक गार्ड क्लॉज माना जा सकता है। फिर, एक आकस्मिक उदाहरण में कोड का आशय स्पष्ट नहीं है और someFunction () के कुछ अन्य दुष्प्रभाव हो सकते हैं या परिणाम का उपयोग // बाकी कोड ... में किया जा सकता है ।
if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;
निम्नलिखित रिफैक्टेड फ़ंक्शन देते हुए:
void string fooBar(string s, int? i) {
if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;
// Rest of code...
return ret;
}
null
बजाय कि अपवाद को स्वीकार करने के बजाय यह क्यों स्वीकार करेंगे कि तर्क स्वीकार नहीं किया गया है?
एक अच्छा कारण मैं सोच सकता हूं कि कोड रखरखाव के लिए: आपके पास निकास का एक बिंदु है। यदि आप परिणाम के प्रारूप को बदलना चाहते हैं, ..., तो इसे लागू करना बहुत सरल है। इसके अलावा, डिबगिंग के लिए, आप वहां एक ब्रेकपॉइंट चिपका सकते हैं :)
यह कहते हुए कि, मुझे एक बार एक पुस्तकालय में काम करना था, जहाँ कोडिंग मानकों ने 'प्रति कार्य एक रिटर्न स्टेटमेंट' लगाया था, और मुझे यह बहुत कठिन लगा। मैं बहुत सारे संख्यात्मक अभिकलन कोड लिखता हूं, और अक्सर 'विशेष मामले' होते हैं, इसलिए कोड का पालन करना काफी कठिन हो गया ...
मल्टीपल एग्जिट पॉइंट छोटे पर्याप्त कार्यों के लिए ठीक हैं - अर्थात्, एक फ़ंक्शन जिसे एक स्क्रीन की लंबाई पर पूरी तरह से देखा जा सकता है। यदि इसी तरह एक लंबा कार्य कई निकास बिंदुओं को शामिल करता है, तो यह संकेत है कि फ़ंक्शन को आगे काटा जा सकता है।
कहा कि मैं कई-बाहर निकलने के कार्यों से बचता हूं जब तक कि बिल्कुल आवश्यक न हो । मैंने बगों का दर्द महसूस किया है जो अधिक जटिल कार्यों में कुछ अस्पष्ट रेखा में कुछ आवारा वापसी के कारण हैं।
मैंने भयानक कोडिंग मानकों के साथ काम किया है, जिसने आप पर एकल निकास पथ को मजबूर किया है और परिणाम लगभग हमेशा असंरचित स्पेगेटी है यदि फ़ंक्शन कुछ भी है, लेकिन तुच्छ है - आप बहुत सारे ब्रेक के साथ समाप्त होते हैं और जारी रखते हैं कि बस रास्ते में मिलता है।
if
कि प्रत्येक विधि कॉल के सामने बयान को छोड़ दें जो सफलता लौटाता है या नहीं :(
एकल निकास बिंदु - अन्य सभी चीजें समान - कोड को काफी अधिक पठनीय बनाती हैं। लेकिन एक पकड़ है: लोकप्रिय निर्माण
resulttype res;
if if if...
return res;
एक नकली है, "res =" "वापसी" से बहुत बेहतर नहीं है। इसमें सिंगल रिटर्न स्टेटमेंट है, लेकिन कई बिंदु जहां फ़ंक्शन वास्तव में समाप्त होता है।
यदि आपके पास कई रिटर्न (या "res =" s) के साथ कार्य किया जाता है, तो अक्सर एक ही निकास बिंदु के साथ इसे कई छोटे कार्यों में तोड़ने का एक अच्छा विचार है।
मेरी सामान्य नीति एक फ़ंक्शन के अंत में केवल एक रिटर्न स्टेटमेंट है जब तक कि कोड की जटिलता बहुत अधिक जोड़कर कम नहीं हो जाती। वास्तव में, मैं बल्कि एफिल का प्रशंसक हूं, जो कि केवल एक रिटर्न नियम लागू करता है, जिसमें कोई रिटर्न स्टेटमेंट नहीं है (अपना रिजल्ट डालने के लिए बस एक ऑटो-रिजल्ट 'चर' है)।
निश्चित रूप से ऐसे मामले हैं जहां कोड को स्पष्ट संस्करण की तुलना में कई रिटर्न के साथ स्पष्ट किया जा सकता है, उनके बिना। कोई यह तर्क दे सकता है कि यदि आपके पास एक ऐसा फ़ंक्शन है, जो कई रिटर्न स्टेटमेंट के बिना समझने योग्य है, तो कई बार काम करने की आवश्यकता होती है, लेकिन कभी-कभी ऐसी चीजों के बारे में व्यावहारिक होना अच्छा होता है।
यदि आप कुछ से अधिक रिटर्न खत्म करते हैं तो आपके कोड में कुछ गड़बड़ हो सकती है। अन्यथा मैं इस बात से सहमत होता हूं कि कभी-कभी यह सबरूटीन में कई स्थानों से लौटने में सक्षम होना अच्छा होता है, खासकर जब यह कोड क्लीनर बनाता है।
sub Int_to_String( Int i ){
given( i ){
when 0 { return "zero" }
when 1 { return "one" }
when 2 { return "two" }
when 3 { return "three" }
when 4 { return "four" }
...
default { return undef }
}
}
इस तरह लिखा जाना बेहतर होगा
@Int_to_String = qw{
zero
one
two
three
four
...
}
sub Int_to_String( Int i ){
return undef if i < 0;
return undef unless i < @Int_to_String.length;
return @Int_to_String[i]
}
ध्यान दें कि यह सिर्फ एक त्वरित उदाहरण था
मैं एक दिशानिर्देश के रूप में अंत में सिंगल रिटर्न के लिए वोट करता हूं। यह कॉमन कोड क्लीन-अप हैंडलिंग में मदद करता है ... उदाहरण के लिए, निम्नलिखित कोड पर एक नज़र डालें ...
void ProcessMyFile (char *szFileName)
{
FILE *fp = NULL;
char *pbyBuffer = NULL:
do {
fp = fopen (szFileName, "r");
if (NULL == fp) {
break;
}
pbyBuffer = malloc (__SOME__SIZE___);
if (NULL == pbyBuffer) {
break;
}
/*** Do some processing with file ***/
} while (0);
if (pbyBuffer) {
free (pbyBuffer);
}
if (fp) {
fclose (fp);
}
}
यह शायद एक असामान्य परिप्रेक्ष्य है, लेकिन मुझे लगता है कि जो कोई भी मानता है कि कई रिटर्न स्टेटमेंट के पक्षधर हैं, उसे कभी भी माइक्रोप्रोसेसर पर डीबगर का उपयोग नहीं करना पड़ता है जो केवल 4 हार्डवेयर ब्रेकप्वाइंट का समर्थन करता है। ;-)
जबकि "एरो कोड" के मुद्दे पूरी तरह से सही हैं, एक मुद्दा जो कई रिटर्न स्टेटमेंट का उपयोग करते समय दूर जाने का लगता है, उस स्थिति में है जहां आप डिबगर का उपयोग कर रहे हैं। ब्रेकपॉइंट लगाने के लिए आपके पास कोई सुविधाजनक कैच-ऑल पोजीशन नहीं है कि आप यह देख सकें कि बाहर निकलने और इसलिए वापसी की स्थिति देखने जा रहे हैं।
किसी फ़ंक्शन में आपके पास जितने अधिक रिटर्न स्टेटमेंट होते हैं, उस एक विधि में उच्च जटिलता। यदि आप अपने आप को आश्चर्यचकित करते हैं कि क्या आपके पास बहुत अधिक रिटर्न स्टेटमेंट हैं, तो आप अपने आप से पूछना चाह सकते हैं कि क्या आपके पास उस फ़ंक्शन में कोड की कई लाइनें हैं।
लेकिन, नहीं, एक / कई रिटर्न स्टेटमेंट में कुछ भी गलत नहीं है। कुछ भाषाओं में, यह दूसरों (C) की तुलना में बेहतर अभ्यास (C ++) है।