यह एक सामान्य स्थिति है और इससे निपटने के कई सामान्य तरीके हैं। यहाँ एक विहित उत्तर पर मेरा प्रयास है। कृपया टिप्पणी करें कि क्या मुझे कुछ याद आया और मैं इस पोस्ट को अद्यतित रखूंगा।
यह एक तीर है
आप जिस पर चर्चा कर रहे हैं, वह तीर विरोधी पैटर्न के रूप में जाना जाता है । इसे एक तीर कहा जाता है क्योंकि नेस्टेड इफ़ की श्रृंखला कोड ब्लॉक बनाती है जो आगे और पीछे दाईं ओर फैलती है और फिर बाईं ओर वापस, एक दृश्य तीर बनाती है जो कोड संपादक फलक के दाईं ओर "अंक" बनाती है।
गार्ड के साथ तीर को समतल करें
एरो से बचने के कुछ सामान्य तरीकों पर यहां चर्चा की गई है । सबसे सामान्य तरीका एक गार्ड पैटर्न का उपयोग करना है , जिसमें कोड पहले अपवाद प्रवाह को संभालता है और फिर मूल प्रवाह को संभालता है, जैसे कि इसके बजाय
if (ok)
{
DoSomething();
}
else
{
_log.Error("oops");
return;
}
... आप उपयोग करेंगे ...।
if (!ok)
{
_log.Error("oops");
return;
}
DoSomething(); //notice how this is already farther to the left than the example above
जब गार्ड्स की एक लंबी श्रृंखला होती है, तो यह कोड काफी हद तक समतल हो जाता है क्योंकि सभी गार्ड बाईं ओर सभी तरह से दिखाई देते हैं और आपके इफ़ेक्ट नस्टेड नहीं होते हैं। इसके अलावा, आप अपनी संबंधित त्रुटि के साथ लॉजिक स्थिति को नेत्रहीन रूप से बाँध रहे हैं, जिससे यह बताना आसान हो जाता है कि क्या हो रहा है:
तीर:
ok = DoSomething1();
if (ok)
{
ok = DoSomething2();
if (ok)
{
ok = DoSomething3();
if (!ok)
{
_log.Error("oops"); //Tip of the Arrow
return;
}
}
else
{
_log.Error("oops");
return;
}
}
else
{
_log.Error("oops");
return;
}
रक्षक:
ok = DoSomething1();
if (!ok)
{
_log.Error("oops");
return;
}
ok = DoSomething2();
if (!ok)
{
_log.Error("oops");
return;
}
ok = DoSomething3();
if (!ok)
{
_log.Error("oops");
return;
}
ok = DoSomething4();
if (!ok)
{
_log.Error("oops");
return;
}
यह उद्देश्यपूर्ण और मात्रात्मक रूप से पढ़ने में आसान है क्योंकि
- किसी दिए गए लॉजिक ब्लॉक के लिए {और} अक्षर एक साथ करीब हैं
- किसी विशेष पंक्ति को समझने के लिए आवश्यक मानसिक संदर्भ की मात्रा छोटी है
- एक शर्त के साथ जुड़े तर्क की संपूर्णता एक पृष्ठ पर होने की अधिक संभावना है
- पेज / आई ट्रैक को स्क्रॉल करने के लिए कोडर की आवश्यकता बहुत कम है
अंत में कॉमन कोड कैसे जोड़ें
गार्ड पैटर्न के साथ समस्या यह है कि इसे "अवसरवादी वापसी" या "अवसरवादी निकास" कहा जाता है पर निर्भर करता है। दूसरे शब्दों में, यह पैटर्न को तोड़ता है कि प्रत्येक और प्रत्येक फ़ंक्शन के पास निकास का बिल्कुल एक बिंदु होना चाहिए। यह दो कारणों से एक समस्या है:
- यह कुछ लोगों को गलत तरीके से रगड़ता है, उदाहरण के लिए पास्कल पर कोड करने वाले लोगों ने सीखा है कि एक कार्य = एक निकास बिंदु।
- यह कोड का एक खंड प्रदान नहीं करता है जो बाहर निकलने पर निष्पादित होता है, चाहे जो भी विषय हो।
नीचे मैंने इस सीमा के आसपास काम करने के लिए या तो भाषा सुविधाओं का उपयोग करके या पूरी तरह से समस्या से बचने के लिए कुछ विकल्प प्रदान किए हैं।
विकल्प 1. आप ऐसा नहीं कर सकते: उपयोग करें finally
दुर्भाग्य से, एक c ++ डेवलपर के रूप में, आप ऐसा नहीं कर सकते। लेकिन यह उन भाषाओं के लिए नंबर एक उत्तर है, जिनमें अंत में कीवर्ड होता है, क्योंकि यह वास्तव में यही है।
try
{
if (!ok)
{
_log.Error("oops");
return;
}
DoSomething(); //notice how this is already farther to the left than the example above
}
finally
{
DoSomethingNoMatterWhat();
}
विकल्प 2. समस्या से बचें: अपने कार्यों को पुनर्स्थापित करें
आप कोड को दो कार्यों में तोड़कर समस्या से बच सकते हैं। इस समाधान में किसी भी भाषा के लिए काम करने का लाभ है, और इसके अतिरिक्त यह चक्रीय जटिलता को कम कर सकता है , जो आपके दोष दर को कम करने का एक सिद्ध तरीका है, और किसी भी स्वचालित इकाई परीक्षणों की विशिष्टता में सुधार करता है।
यहाँ एक उदाहरण है:
void OuterFunction()
{
DoSomethingIfPossible();
DoSomethingNoMatterWhat();
}
void DoSomethingIfPossible()
{
if (!ok)
{
_log.Error("Oops");
return;
}
DoSomething();
}
विकल्प 3. भाषा चाल: एक नकली लूप का उपयोग करें
एक और आम ट्रिक जो मैं देख रहा हूँ (सच) और ब्रेक का उपयोग कर रहा हूँ, जैसा कि अन्य उत्तरों में दिखाया गया है।
while(true)
{
if (!ok) break;
DoSomething();
break; //important
}
DoSomethingNoMatterWhat();
हालांकि यह प्रयोग करने की तुलना में कम "ईमानदार" है goto
, फिर भी रिफैक्टिंग करते समय गड़बड़ होने की संभावना कम है, क्योंकि यह स्पष्ट रूप से तर्क की गुंजाइश की सीमाओं को चिह्नित करता है। एक भोली कोडर जो आपके लेबल को काटती है और चिपकाती है या आपके goto
कथन बड़ी समस्याएँ पैदा कर सकते हैं! (और स्पष्ट रूप से पैटर्न बहुत आम है अब मुझे लगता है कि यह स्पष्ट रूप से इरादे का संचार करता है, और इसलिए "बेईमान" बिल्कुल नहीं है)।
इस विकल्प के अन्य रूप हैं। उदाहरण के लिए, एक के switch
बजाय उपयोग कर सकते हैं while
। break
कीवर्ड के साथ कोई भी भाषा निर्माण शायद काम करेगा।
विकल्प 4. वस्तु जीवन चक्र का लाभ उठाएं
एक अन्य दृष्टिकोण वस्तु जीवन चक्र का लाभ उठाता है। अपने पैरामीटर्स के चारों ओर ले जाने के लिए एक संदर्भ ऑब्जेक्ट का उपयोग करें (ऐसा कुछ जो हमारे भोले उदाहरण में संदिग्ध रूप से कमी है) और जब आप काम कर रहे हों तो इसका निपटान करें।
class MyContext
{
~MyContext()
{
DoSomethingNoMatterWhat();
}
}
void MainMethod()
{
MyContext myContext;
ok = DoSomething(myContext);
if (!ok)
{
_log.Error("Oops");
return;
}
ok = DoSomethingElse(myContext);
if (!ok)
{
_log.Error("Oops");
return;
}
ok = DoSomethingMore(myContext);
if (!ok)
{
_log.Error("Oops");
}
//DoSomethingNoMatterWhat will be called when myContext goes out of scope
}
नोट: सुनिश्चित करें कि आप अपनी पसंद की भाषा के वस्तु जीवन चक्र को समझते हैं। काम करने के लिए आपको किसी प्रकार के नियतात्मक कचरा संग्रह की आवश्यकता होती है, अर्थात आपको यह जानना होगा कि विध्वंसक कब कहा जाएगा। कुछ भाषाओं में आपको Dispose
एक विध्वंसक के बजाय उपयोग करने की आवश्यकता होगी ।
विकल्प 4.1। ऑब्जेक्ट जीवन चक्र (आवरण पैटर्न) का लाभ उठाएं
यदि आप एक वस्तु-उन्मुख दृष्टिकोण का उपयोग करने जा रहे हैं, तो यह सही हो सकता है। यह विकल्प संसाधनों को "रैप" करने के लिए एक वर्ग का उपयोग करता है जिसे सफाई की आवश्यकता होती है, साथ ही साथ इसके अन्य संचालन भी।
class MyWrapper
{
bool DoSomething() {...};
bool DoSomethingElse() {...}
void ~MyWapper()
{
DoSomethingNoMatterWhat();
}
}
void MainMethod()
{
bool ok = myWrapper.DoSomething();
if (!ok)
_log.Error("Oops");
return;
}
ok = myWrapper.DoSomethingElse();
if (!ok)
_log.Error("Oops");
return;
}
}
//DoSomethingNoMatterWhat will be called when myWrapper is destroyed
फिर से, सुनिश्चित करें कि आप अपने जीवन चक्र को समझते हैं।
विकल्प 5. भाषा की चाल: शॉर्ट-सर्किट मूल्यांकन का उपयोग करें
शॉर्ट सर्किट मूल्यांकन का लाभ उठाने के लिए एक और तकनीक है ।
if (DoSomething1() && DoSomething2() && DoSomething3())
{
DoSomething4();
}
DoSomethingNoMatterWhat();
यह समाधान && ऑपरेटर के कार्य करने के तरीके का लाभ उठाता है। जब बाएं हाथ की ओर && झूठ का मूल्यांकन करता है, तो दाहिने हाथ की तरफ का मूल्यांकन कभी नहीं किया जाता है।
कॉम्पैक्ट कोड की आवश्यकता होने पर यह ट्रिक सबसे अधिक उपयोगी होती है और जब कोड में बहुत अधिक रखरखाव देखने की संभावना नहीं होती है, जैसे कि आप एक प्रसिद्ध एल्गोरिथ्म को लागू कर रहे हैं। अधिक सामान्य कोडिंग के लिए इस कोड की संरचना बहुत भंगुर है; यहां तक कि तर्क में मामूली बदलाव भी कुल पुनर्लेखन को गति प्रदान कर सकता है।