क्या मुझे भविष्य में जरूरत पड़ने पर सिर्फ अनावश्यक कोड जोड़ना चाहिए?


114

सही या गलत तरीके से, मैं वर्तमान में इस विश्वास के साथ हूं कि मुझे हमेशा अपने कोड को यथासंभव मजबूत बनाने की कोशिश करनी चाहिए, भले ही इसका मतलब अनावश्यक कोड / चेक में जोड़ना हो जो मुझे पता है कि अभी किसी काम का नहीं होगा, लेकिन वे रेखा से नीचे वर्षों की x राशि हो सकती है।

उदाहरण के लिए, मैं वर्तमान में एक मोबाइल एप्लिकेशन पर काम कर रहा हूं जिसमें यह कोड है:

public static CalendarRow AssignAppointmentToRow(Appointment app, List<CalendarRow> rows)
{
    //1. Is rows equal to null? - This will be the case if this is the first appointment.
    if (rows == null) {
        rows = new List<CalendarRow> ();
    }

    //2. Is rows empty? - This will be the case if this is the first appointment / some other unknown reason.
    if(rows.Count == 0)
    {
        rows.Add (new CalendarRow (0));
        rows [0].Appointments.Add (app);
    }

    //blah...
}

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

हालांकि, भविष्य में एक मामला हो सकता है जहां यह दूसरा ifबयान वास्तव में आवश्यक है, और एक ज्ञात कारण के लिए।

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

दूसरी ओर, ऐसे कई उदाहरण भी हैं जहां इस तरह के कोड ने नए व्यवहारों के साथ कोड को बढ़ाने के लिए इसे बहुत आसान बना दिया है, क्योंकि मुझे वापस जाने की ज़रूरत नहीं है और यह सुनिश्चित करना है कि सभी संबंधित चेक ठीक हैं।

क्या इस तरह के कोड के लिए कोई सामान्य नियम-आधारित दिशा - निर्देश हैं? (मुझे यह सुनने में दिलचस्पी होगी कि क्या यह अच्छा या बुरा अभ्यास माना जाएगा?)

NB: यह इस सवाल के समान माना जा सकता है , हालांकि उस सवाल के विपरीत, मुझे लगता है कि कोई समय सीमा नहीं है एक जवाब चाहते हैं।

TLDR: क्या मुझे भविष्य में इसे और अधिक मजबूत बनाने के लिए निरर्थक कोड जोड़ने के लिए इतनी दूर जाना चाहिए?



95
सॉफ्टवेयर विकास की चीजों में जो हर समय नहीं होनी चाहिए।
रोमन रेनर

34
यदि आप जानते हैं कि if(rows.Count == 0)ऐसा कभी नहीं होगा, तो आप ऐसा होने पर एक अपवाद उठा सकते हैं - और जांच करें कि आपकी धारणा गलत क्यों हो गई।
मारुत

9
सवाल से अप्रासंगिक, लेकिन मुझे संदेह है कि आपके कोड में एक बग है। जब पंक्तियाँ शून्य होती हैं तो एक नई सूची बनाई जाती है और (मैं अनुमान लगाता हूं) को फेंक दिया जाता है। लेकिन जब पंक्तियाँ शून्य नहीं होती हैं, तो एक मौजूदा सूची बदल जाती है। एक बेहतर डिजाइन इस बात पर जोर दे सकता है कि ग्राहक एक सूची में पास हो जाए जो खाली हो या न हो।
थिओडोर नॉरवेल

9
क्यों rowsकभी अशक्त होगा? कम से कम .NET में, संग्रह अच्छा होने के लिए कोई अच्छा कारण नहीं है। खाली , निश्चित, लेकिन अशक्त नहीं । मैं एक अपवाद फेंक दूंगा यदि rowsअशक्त है, क्योंकि इसका मतलब है कि कॉल करने वाले द्वारा तर्क में एक चूक है।
किरालासे

जवाबों:


176

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

पहली स्थिति ए और दूसरी स्थिति बी को बुलाओ।

आप पहले कहते हैं:

विशेष रूप से खंड दो को देखते हुए, मुझे पता है कि यदि खंड एक सत्य है, तो खंड दो भी सत्य होगा।

वह है: A का तात्पर्य B, या अधिक मूल शब्दों में है (NOT A) OR B

और तब:

मैं किसी भी कारण के बारे में नहीं सोच सकता कि क्यों खंड एक गलत होगा और खंड दो सही मूल्यांकन करता है, जो दूसरे को बयान बेमानी बनाता है।

यही कारण है: NOT((NOT A) AND B)(NOT B) OR Aजो B B का अर्थ है, पाने के लिए Demagon का नियम लागू करें ।

इसलिए, यदि आपके दोनों कथन सत्य हैं, तो A का अर्थ है B और B का अर्थ है A, जिसका अर्थ है कि वे समान होना चाहिए।

इसलिए, चेक निरर्थक हैं। आपको प्रोग्राम के माध्यम से चार कोड पथ दिखाई देते हैं लेकिन वास्तव में आपके पास केवल दो हैं।

तो अब सवाल यह है कि कोड कैसे लिखें? असली सवाल यह है: विधि का कथित अनुबंध क्या है ? यह सवाल कि क्या स्थितियां बेमानी हैं, एक लाल हेरिंग है। असली सवाल यह है कि "क्या मैंने एक समझदार अनुबंध तैयार किया है, और क्या मेरा तरीका स्पष्ट रूप से उस अनुबंध को लागू करता है?"

आइए घोषणा को देखें:

public static CalendarRow AssignAppointmentToRow(
    Appointment app,    
    List<CalendarRow> rows)

यह सार्वजनिक है, इसलिए इसे मनमाने कॉल करने वालों से खराब डेटा के लिए मजबूत होना चाहिए।

यह एक मूल्य लौटाता है, इसलिए यह इसके वापसी मूल्य के लिए उपयोगी होना चाहिए, इसके दुष्प्रभाव के लिए नहीं।

और फिर भी विधि का नाम एक क्रिया है, यह सुझाव देता है कि यह इसके दुष्प्रभाव के लिए उपयोगी है।

सूची पैरामीटर का अनुबंध है:

  • एक अशक्त सूची ठीक है
  • इसमें एक या अधिक तत्वों के साथ एक सूची ठीक है
  • इसमें बिना तत्वों वाले एक सूची गलत है और यह संभव नहीं होना चाहिए।

यह अनुबंध पागल है । इसके लिए दस्तावेज लिखने की कल्पना करें! लिखने के मामले की कल्पना करो!

मेरी सलाह: शुरू करो। इस एपीआई में कैंडी मशीन इंटरफेस है जो इस पर लिखा है। (अभिव्यक्ति Microsoft में कैंडी मशीनों के बारे में एक पुरानी कहानी से है, जहां मूल्य और चयन दोनों दो-अंकीय संख्या हैं, और "85" टाइप करना बहुत आसान है जो कि आइटम 75 की कीमत है, और आपको मिलता है गलत आइटम। मजेदार तथ्य: हां, मैंने वास्तव में वह गलती की है जब मैं माइक्रोसॉफ्ट के साथ एक वेंडिंग मशीन से गोंद प्राप्त करने की कोशिश कर रहा था!)

यहाँ एक समझदार अनुबंध डिजाइन करने का तरीका बताया गया है:

अपने तरीके को इसके साइड इफ़ेक्ट या इसके रिटर्न वैल्यू दोनों के लिए उपयोगी बनाएं, न कि दोनों के लिए।

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

कभी भी अशक्तियाँ स्वीकार न करें; एक अशक्त अनुक्रम एक घृणा है। एक खाली अनुक्रम को पारित करने के लिए कॉलर की आवश्यकता होती है अगर यह समझ में आता है, कभी भी अशक्त नहीं।

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

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

अब, मैंने केवल आपके विशिष्ट मामले के बारे में बात की है, और आपने एक सामान्य प्रश्न पूछा है। तो यहाँ कुछ अतिरिक्त सामान्य सलाह है:

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

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

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

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


24
मैंने पहले कभी इस तरह के तरीकों के बारे में कभी नहीं सोचा था, इसके बारे में सोचकर अब मैं हमेशा हर घटना को कवर करने की कोशिश करता हूं, जब वास्तव में अगर कोई व्यक्ति / पद्धति मेरे तरीके को कॉल नहीं करती है, तो मुझे वह नहीं चाहिए, जो मुझे चाहिए। ' उनकी गलतियों को ठीक करने का प्रयास नहीं किया जाता है (जब मुझे वास्तव में पता नहीं है कि उनका इरादा क्या है), मुझे बस दुर्घटनाग्रस्त होना चाहिए। इसके लिए धन्यवाद, एक मूल्यवान सबक सीखा गया है!
किडकोड

4
तथ्य यह है कि एक विधि एक मूल्य देता है इसका मतलब यह नहीं है कि इसके साइड-इफेक्ट के लिए भी उपयोगी नहीं होना चाहिए। समवर्ती कोड में, दोनों को वापस करने के लिए फ़ंक्शन की क्षमता अक्सर महत्वपूर्ण होती है ( CompareExchangeऐसी क्षमता के बिना कल्पना करें !), और यहां तक ​​कि गैर-समवर्ती परिदृश्यों में भी "कुछ रिकॉर्ड करें जैसे कि यह मौजूद नहीं है, और पास-इन करें रिकॉर्ड या जो अस्तित्व में था "किसी भी दृष्टिकोण से अधिक सुविधाजनक हो सकता है जो साइड-इफ़ेक्ट और रिटर्न वैल्यू दोनों को नियोजित नहीं करता है।
सुपरकाट

5
@KidCode हाँ, एरिक वास्तव में चीजों को स्पष्ट करने में अच्छा है, यहां तक ​​कि कुछ वास्तव में जटिल विषय भी। :)
मेसन व्हीलर

3
@ सुपरकैट ज़रूर, लेकिन इसके बारे में तर्क करना भी मुश्किल है। सबसे पहले, आप शायद एक ऐसी चीज़ को देखना चाहते हैं जो वैश्विक स्थिति को संशोधित नहीं करती है, इस प्रकार दोनों संगामिति मुद्दों और राज्य भ्रष्टाचार से बचते हैं। जब यह उचित नहीं होता है, तब भी आप दोनों को अलग-थलग करना चाहते हैं - जो आपको स्पष्ट स्थान देता है जहां संगामिति एक मुद्दा है (और इस तरह इसे अतिरिक्त खतरनाक माना जाता है), और ऐसे स्थान जहां इसे संभाला जाता है। यह मूल ओओपी पेपर में मुख्य विचारों में से एक था, और अभिनेताओं की उपयोगिता के मूल में है। कोई धार्मिक नियम नहीं - सिर्फ दो को अलग करना पसंद करते हैं जब यह समझ में आता है। यह आमतौर पर करता है।
लुआॅन

5
यह सभी के लिए बहुत उपयोगी पोस्ट है!
एड्रियन बुज़िया

89

आप जो कोड ऊपर दिखा रहे हैं उसमें आप क्या कर रहे हैं यह भविष्य के प्रमाण के रूप में इतना नहीं है जितना कि यह रक्षात्मक कोडिंग है

दोनों ifबयान अलग-अलग चीजों के लिए परीक्षण करते हैं। आपकी आवश्यकताओं के आधार पर दोनों उपयुक्त परीक्षण हैं।

किसी nullवस्तु के लिए धारा 1 का परीक्षण और उसे सही करता है । साइड नोट: सूची बनाना किसी भी बाल आइटम (जैसे, CalendarRow) का निर्माण नहीं करता है ।

उपयोगकर्ता और / या कार्यान्वयन त्रुटियों के लिए धारा 2 परीक्षण और सुधार करता है। सिर्फ इसलिए कि आपके पास List<CalendarRow>यह नहीं है कि आपके पास सूची में कोई आइटम है। उपयोगकर्ता और कार्यान्वयनकर्ता ऐसी चीजें करेंगे जिनकी आप कल्पना नहीं कर सकते हैं क्योंकि उन्हें इसे करने की अनुमति है, चाहे वह आपके लिए समझ में आता हो या नहीं।


1
वास्तव में मैं 'बेवकूफ यूजर ट्रिक्स' ले रहा हूं मतलब इनपुट। हाँ, आपको कभी भी इनपुट पर भरोसा नहीं करना चाहिए। अपनी कक्षा के बाहर से भी। मान्य करें! अगर आपको लगता है कि यह केवल एक भविष्य की चिंता है तो मुझे आज आपको हैक करके खुशी होगी।
कैंडिड_ऑरेंज

1
@CandiedOrange, यही इरादा था, लेकिन शब्दांकन ने हास्य को व्यक्त नहीं किया। मैंने शब्दांकन बदल दिया है।
एडम जुकरमैन

3
यहाँ एक त्वरित प्रश्न है, अगर यह एक कार्यान्वयन त्रुटि / खराब डेटा है, तो क्या मुझे उनकी त्रुटियों को ठीक करने की कोशिश करने के बजाय, बस दुर्घटनाग्रस्त नहीं होना चाहिए?
किडकोड

4
@ किडकोड जब भी आप "उनकी त्रुटियों को ठीक करने" का प्रयास करते हैं, तो आपको दो चीजें करने की जरूरत है, एक ज्ञात अच्छी स्थिति में लौटें और चुपचाप मूल्यवान इनपुट न खोएं। इस मामले में उस नियम का पालन करने से सवाल उठता है: क्या एक शून्य पंक्ति सूची मूल्यवान इनपुट है?
कैंडिड_ऑरेंज

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

35

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

मैं व्यक्तिगत रूप से भविष्य के लिए कोड बुलेट-प्रूफ बनाने की जहमत नहीं उठाऊंगा। मुझे भविष्य का पता नहीं है, और इस तरह, भविष्य में किसी भी तरह के "भविष्य के बुलेट-प्रूफ" कोड को बुरी तरह से विफल होने के लिए बर्बाद किया जाता है।

इसके बजाय, मैं एक अलग दृष्टिकोण पसंद करूंगा: ऐसी धारणाएँ बनाएं जो आप assert()मैक्रो या समान सुविधाओं का उपयोग करके स्पष्ट करते हैं । इस तरह, जब भविष्य दरवाजे के चारों ओर आता है, तो यह आपको ठीक-ठीक बताएगा कि आपकी धारणाएं कहां और कितनी हैं।


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

3
@ किडकोड: अच्छा अवलोकन। यहां आपकी खुद की सोच वास्तव में आपके द्वारा स्वीकार किए गए कई उत्तरों की तुलना में बहुत अधिक स्मार्ट है।
जैक्सबी

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

23

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

इसे साहसपूर्वक लगाने के लिए, यदि आपके कार्यक्रम में कोई छोटी सी भी त्रुटि है, तो आप चाहते हैं कि आप देखते समय पूरी तरह से दुर्घटनाग्रस्त हो जाएँ!

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

अधिक विचारों के लिए, इस लघु निबंध को पढ़ें: डोन्ट नेल योर प्रोग्राम इन द अपरसिट पोज़िशन


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

// Calculates the nth Fibonacci number
int fibonacci(int n) {
    int a = 0;
    int b = 1;

    for(int i = 0; i < n; i++) {
        int temp = b;
        b = a + b;
        a = temp;
    }

    return b;
}

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

यह इस तरह समारोह लिखने के लिए आकर्षक हो सकता है:

// Calculates the nth Fibonacci number
int fibonacci(int n) {
    int a = 0;
    int b = 1;

    // Make sure the input is nonnegative
    if(n < 0) {
        n = 1; // Replace the negative input with an input that will work
    }

    for(int i = 0; i < n; i++) {
        int temp = b;
        b = a + b;
        a = temp;
    }

    return b;
}

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

इसके बजाय, इस तरह से चेक लिखना बेहतर है:

// Calculates the nth Fibonacci number
int fibonacci(int n) {
    int a = 0;
    int b = 1;

    // Make sure the input is nonnegative
    if(n < 0) {
        throw new ArgumentException("Can't have negative inputs to Fibonacci");
    }

    for(int i = 0; i < n; i++) {
        int temp = b;
        b = a + b;
        a = temp;
    }

    return b;
}

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


1
क्या अवैध तर्क या आउट-ऑफ-रेंज तर्क को इंगित करने के लिए c # के पास एक विशेष प्रकार का अपवाद नहीं है?
JDługosz

@ JDługosz हां! C # में एक ArgumentException है , और Java में एक IllegalArgumentException है
केविन

प्रश्न # का उपयोग कर रहा था। पूर्णता के लिए यहां C ++ (सारांश का लिंक) दिया गया है।
13:30 पर JDługosz

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

2
@ Luaan: हालांकि यह उदाहरण समारोह की जिम्मेदारी नहीं है।
whatsisname

11

क्या आपको निरर्थक कोड जोड़ना चाहिए? नहीं।

लेकिन, आप जो वर्णन करते हैं, वह निरर्थक कोड नहीं है

आप जो वर्णन करते हैं, वह आपके फ़ंक्शन की पूर्व शर्त का उल्लंघन करने वाले कॉलिंग कोड के खिलाफ रक्षात्मक रूप से प्रोग्रामिंग कर रहा है। चाहे आप ऐसा करते हैं या केवल प्रलेखन पढ़ने के लिए उपयोगकर्ताओं को छोड़ देते हैं और स्वयं उन उल्लंघनों से बचते हैं, पूरी तरह से व्यक्तिपरक है।

निजी तौर पर, मैं इस कार्यप्रणाली में एक बड़ा विश्वासी हूं, लेकिन जैसा कि आप को सतर्क रहना होगा। उदाहरण के लिए, C ++ को लें std::vector::operator[]। वीएस के डिबग-मोड कार्यान्वयन को एक पल के लिए अलग रख दें, तो यह फ़ंक्शन सीमा की जाँच नहीं करता है। यदि आप किसी ऐसे तत्व का अनुरोध करते हैं जो मौजूद नहीं है, तो परिणाम अपरिभाषित है। यह वैध वेक्टर इंडेक्स प्रदान करने के लिए उपयोगकर्ता पर निर्भर है। यह काफी जानबूझकर है: आप इसे कॉलसाइट में जोड़कर चेक करने के लिए "इन" चुन सकते हैं, लेकिन यदि operator[]कार्यान्वयन इसे निष्पादित करना था तो आप "ऑप्ट आउट" नहीं कर पाएंगे। एक काफी निम्न-स्तरीय फ़ंक्शन के रूप में, यह समझ में आता है।

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

अगर मुझे एक सामान्य नियम (हालांकि, एक सामान्य नियम के रूप में, मैं उन लोगों से बचने की कोशिश करता हूं) के साथ आना था, तो मैं कहूंगा कि एक फ़ंक्शन जो निम्नलिखित में से किसी को संतुष्ट करता है:

  • एक उच्च-स्तरीय भाषा में कहते हैं (सी के बजाय जावास्क्रिप्ट)
  • एक इंटरफ़ेस सीमा पर बैठता है
  • प्रदर्शन-महत्वपूर्ण नहीं है
  • सीधे उपयोगकर्ता इनपुट को स्वीकार करता है

… रक्षात्मक प्रोग्रामिंग से लाभ उठा सकते हैं। अन्य मामलों में, आप अभी भी assertआयनों को लिख सकते हैं जो परीक्षण के दौरान आग लगाते हैं लेकिन रिलीज बिल्ड में अक्षम हो जाते हैं, जिससे बग को खोजने की आपकी क्षमता बढ़ जाती है।

इस विषय को विकिपीडिया ( https://en.wikipedia.org/wiki/Defensive_programming ) पर और विस्तार से देखा गया है ।


9

दस प्रोग्रामिंग कमांड में से दो यहां प्रासंगिक हैं:

  • आप यह नहीं मानेंगे कि इनपुट सही है

  • तू भविष्य के उपयोग के लिए कोड नहीं बनायेगा

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

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


5
ये प्रोग्रामिंग कमांड कहां हैं। क्या आपके पास एक लिंक है? मैं उत्सुक हूं क्योंकि मैंने कई कार्यक्रमों पर काम किया है जो उन आदेशों में से दूसरे की सदस्यता लेते हैं, और कई जो नहीं करते थे। वास्तव में, जिन लोगों ने कमांड को सदस्यता दी थी, वे कमांड के सहज तर्क के बावजूद, जल्द ही स्थिरता केThou shall not make code for future use मुद्दों में भाग गए । मैंने पाया है, वास्तविक जीवन में कोडिंग, यह कमांड केवल कोड में प्रभावी है जहां आप फीचर सूची और समय सीमा को नियंत्रित करते हैं, और सुनिश्चित करें कि आपको उन तक पहुंचने के लिए भविष्य की तलाश कोड की आवश्यकता नहीं है ... जो कहना है, कभी नहीं।
Cort Ammon

1
तुच्छ रूप से सिद्ध: यदि कोई भविष्य के उपयोग की संभावना का अनुमान लगा सकता है, और "भविष्य के उपयोग" कोड का अनुमानित मूल्य, और उन दोनों का उत्पाद "भविष्य के उपयोग" कोड को जोड़ने की लागत से अधिक है, तो यह सांख्यिकीय रूप से इष्टतम है कोड जोड़ें। मुझे लगता है कि कमान उन परिस्थितियों में दिखाई देती है जहां डेवलपर्स (या प्रबंधक) यह स्वीकार करने के लिए मजबूर होते हैं कि उनका अनुमान कौशल उतना विश्वसनीय नहीं है जितना वे चाहेंगे, इसलिए एक रक्षात्मक उपाय के रूप में वे भविष्य के कार्य का अनुमान लगाने के लिए बिल्कुल नहीं चुनते हैं।
Cort Ammon

2
@CortAmmon प्रोग्रामिंग में धार्मिक आदेशों के लिए कोई जगह नहीं है - "सर्वोत्तम अभ्यास" केवल संदर्भ में समझ में आता है, और तर्क के बिना "सर्वोत्तम अभ्यास" सीखना आपको अनुकूलित करने में असमर्थ बनाता है। मैंने YAGNI को बहुत उपयोगी पाया है, लेकिन इसका मतलब यह नहीं है कि मैं उन जगहों के बारे में नहीं सोचता जहां बाद में विस्तार बिंदुओं को जोड़ना महंगा होगा - इसका मतलब है कि मुझे समय से पहले सरल मामलों के बारे में सोचने की ज़रूरत नहीं है। बेशक, यह समय के साथ भी बदलता है, क्योंकि आपके कोड पर अधिक से अधिक धारणाएं प्रभावी हो जाती हैं, जिससे आपके कोड का इंटरफ़ेस प्रभावी रूप से बढ़ जाता है - यह एक संतुलनकारी कार्य है।
लुआॅन

2
@CortAmmon आपका "तुच्छ रूप से सिद्ध" मामला दो बहुत महत्वपूर्ण लागतों को नजरअंदाज करता है - अनुमान की लागत, और (संभवतः अनावश्यक) विस्तार बिंदु के रखरखाव की लागत। यही कारण है कि लोगों को अनुमानों को कम करके आंका जा सकता है। एक बहुत ही सरल सुविधा के लिए, यह कुछ सेकंड के लिए सोचने के लिए पर्याप्त हो सकता है, लेकिन यह काफी संभावना है कि आपको शुरू में "सरल" सुविधा से आने वाले कीड़े की एक पूरी कैन मिल जाएगी। संचार कुंजी है - जैसे-जैसे चीजें बड़ी होती जाती हैं, आपको अपने नेता / ग्राहक के साथ बात करने की आवश्यकता होती है।
लुआं

1
@ Luaan मैं आपकी बात के लिए बहस करने की कोशिश कर रहा था, प्रोग्रामिंग में धार्मिक आदेशों के लिए कोई जगह नहीं है। जब तक एक व्यावसायिक मामला मौजूद है, जहां विस्तार बिंदु के आकलन और रखरखाव की लागत पर्याप्त रूप से बंधी हुई है, एक ऐसा मामला है जहां कहा गया है कि "आज्ञा" संदिग्ध है। कोड के साथ मेरे अनुभव से, इस तरह के विस्तार बिंदुओं को छोड़ने या न करने का सवाल एक पंक्ति या दूसरे तरीके से कभी भी अच्छी तरह से फिट नहीं हुआ है।
Cort Ammon

7

'निरर्थक कोड' और 'YAGNI' की परिभाषा अक्सर मुझे इस बात पर निर्भर करती है कि आप आगे कितनी दूर देखते हैं।

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

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

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


6

मापदंडों के बारे में किसी भी धारणा को प्रलेखित करना एक अच्छा विचार है। और यह जांचना एक अच्छा विचार है कि आपका क्लाइंट कोड उन मान्यताओं का उल्लंघन नहीं करता है। मैं यह करूँगा:

/** ...
*   Precondition: rows is null or nonempty
*/
public static CalendarRow AssignAppointmentToRow(Appointment app, List<CalendarRow> rows)
{
    Assert( rows==null || rows.Count > 0 )
    //1. Is rows equal to null? - This will be the case if this is the first appointment.
    if (rows == null) {
        rows = new List<CalendarRow> ();
        rows.Add (new CalendarRow (0));
        rows [0].Appointments.Add (app);
    }

    //blah...
}

[यह मानते हुए कि C # है, Assert नौकरी के लिए सबसे अच्छा साधन नहीं हो सकता है, क्योंकि यह जारी किए गए कोड में संकलित नहीं है। लेकिन यह एक और दिन के लिए बहस है।]

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


5

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

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

इस संभावना का अनुमान लगाएं कि अतिरिक्त कोड की वास्तव में आवश्यकता होगी। फिर गणित करते हैं।

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


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

आपको उन बगों की लागत का अनुमान लगाने की आवश्यकता है जो कार्यक्रम में रेंग सकते हैं क्योंकि आप अमान्य इनपुट पर विफल नहीं होते हैं। लेकिन आप बग्स की लागत का अनुमान नहीं लगा सकते क्योंकि परिभाषा से वे अप्रत्याशित हैं। तो पूरा "गणित करते हैं" अलग हो जाता है।
जैक्सबी

3

यहाँ प्राथमिक सवाल यह है कि "क्या होगा यदि आप करते हैं / नहीं"?

जैसा कि दूसरों ने बताया है, इस तरह की रक्षात्मक प्रोग्रामिंग अच्छी है, लेकिन यह कभी-कभी खतरनाक भी होती है।

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

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

आपके सभी "अनावश्यक" निर्णय उस आधार को ध्यान में रखकर किए जाने चाहिए।


2

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

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

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

if (rows == null) throw new ArgumentNullException(nameof(rows));

आप एक इनपुट नहीं चाहतेrows हैं जहां अशक्त हैं, और आप जितनी जल्दी हो सके अपने सभी कॉलर्स को त्रुटि स्पष्ट करना चाहते हैं

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

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

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

इसका मतलब यह नहीं है कि अप्रत्याशित सब कुछ आपके आवेदन को दुर्घटनाग्रस्त कर दे, इसके विपरीत। यदि आप अपवादों का अच्छी तरह से उपयोग करते हैं, तो वे स्वाभाविक रूप से सुरक्षित त्रुटि हैंडलिंग बिंदु बनाते हैं, जहां आप स्थिर एप्लिकेशन स्थिति को पुनर्स्थापित कर सकते हैं और उपयोगकर्ता को वे जारी रखने की अनुमति दे सकते हैं (आदर्श रूप में उपयोगकर्ता के लिए किसी भी डेटा हानि से बचने के दौरान)। उन हैंडलिंग बिंदुओं पर, आपको समस्याओं को ठीक करने के अवसर दिखाई देंगे ("कोई क्रम संख्या 2212 नहीं मिला। क्या आपका मतलब 2212b है?") या उपयोगकर्ता नियंत्रण दे ("डेटाबेस से जुड़ने में त्रुटि। फिर से प्रयास करें?")। यहां तक ​​कि जब कोई ऐसा विकल्प उपलब्ध नहीं है, तो भी कम से कम यह आपको एक मौका देगा कि कुछ भी भ्रष्ट नहीं हुआ - मैंने उपयोग करने वाले कोड की सराहना करना शुरू कर दिया है usingऔर try... finallyबहुत अधिक try...catch, यह आपको असाधारण परिस्थितियों में भी आक्रमणकारियों को बनाए रखने के लिए बहुत सारे अवसर देता है।

उपयोगकर्ताओं को अपना डेटा और काम नहीं खोना चाहिए। यह अभी भी विकास लागत आदि के साथ संतुलित किया जाना है, लेकिन यह एक बहुत अच्छा सामान्य दिशानिर्देश है (यदि उपयोगकर्ता तय करते हैं कि आपके सॉफ़्टवेयर को खरीदना है या नहीं - आंतरिक सॉफ़्टवेयर में आमतौर पर वह लक्जरी नहीं है)। यहां तक ​​कि पूरे एप्लिकेशन क्रैश होने की समस्या बहुत कम हो जाती है यदि उपयोगकर्ता बस फिर से शुरू कर सकता है और वापस वही कर सकता है जो वे कर रहे थे। यह वास्तविक मजबूती है - डिस्क पर अपने दस्तावेज़ को भ्रष्ट किए बिना , और आपको एक विकल्प देने के लिए हर समय अपने काम को सहेजने वाला शब्दक्रैश के बाद Word को पुनरारंभ करने के बाद उन परिवर्तनों को पुनर्स्थापित करने के लिए। क्या यह पहली जगह में बग से बेहतर है? शायद नहीं - हालांकि यह मत भूलो कि एक दुर्लभ बग को पकड़ने में खर्च किए गए काम को हर जगह बेहतर खर्च किया जा सकता है। लेकिन यह विकल्पों की तुलना में बहुत बेहतर है - उदाहरण के लिए डिस्क पर एक दूषित दस्तावेज़, अंतिम सहेजे जाने के बाद से सभी काम, दस्तावेज़ को क्रैश होने से पहले ऑटो-बदल दिया गया जो कि बस Ctrl + A और हटाएं गया।


1

मैं आपकी धारणा के आधार पर इसका जवाब देने जा रहा हूं कि मजबूत कोड आपको अब से "वर्षों" का लाभ देगा। यदि दीर्घकालिक लाभ आपका लक्ष्य है, तो मैं डिजाइन और मजबूती पर प्राथमिकता को प्राथमिकता दूंगा।

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

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

इसलिए यदि आपको लगता है कि आपका ऐप डिज़ाइन किया गया है और साथ ही इसे बनाए रखा जा सकता है, तो इसे बनाए रखना आसान है और इसमें कोई बग नहीं है, आपको जो कोड की आवश्यकता है उसे जोड़ने की तुलना में कुछ बेहतर करना है। यह कम से कम आप व्यर्थ सुविधाओं पर लंबे समय तक काम करने वाले अन्य सभी डेवलपर्स के लिए सम्मान से बाहर कर सकते हैं।


1

नहीं आपको नहीं करना चाहिए। और आप वास्तव में अपने ही सवाल का जवाब दे रहे हैं जब आप कहते हैं कि कोडिंग का यह तरीका बग को छिपा सकता है । यह कोड को अधिक मजबूत नहीं बनाएगा - बल्कि यह बगों के लिए अधिक प्रवण बना देगा और डीबगिंग को कठिन बना देगा।

आप अपनी वर्तमान उम्मीदों को rowsतर्क के बारे में बताते हैं: या तो यह अशक्त है या अन्यथा इसमें कम से कम एक आइटम है। तो सवाल यह है: क्या यह एक अच्छा विचार है कि कोड को लिखना अनपेक्षित रूप से तीसरे मामले को संभालना है जहां rowsशून्य आइटम हैं?

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

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

तो आपको कोड इस तरह दिखना चाहिए:

public static CalendarRow AssignAppointmentToRow(Appointment app, List<CalendarRow> rows)
{
    if (rows != null && rows.Count == 0) throw new ArgumentException("Rows is empty."); 

    //1. Is rows equal to null? - This will be the case if this is the first appointment.
    if (rows == null) {
        rows = new List<CalendarRow> ();
        rows.Add (new CalendarRow (0));
        rows [0].Appointments.Add (app);
    }

    //blah...
}

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


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


1

क्या मुझे भविष्य में जरूरत पड़ने पर सिर्फ अनावश्यक कोड जोड़ना चाहिए?

आपको किसी भी समय अनावश्यक कोड नहीं जोड़ना चाहिए।

आपको उस कोड को नहीं जोड़ना चाहिए जो केवल भविष्य में आवश्यक हो।

आपको यह सुनिश्चित करना चाहिए कि आपका कोड अच्छा व्यवहार करे चाहे कुछ भी हो जाए।

"अच्छी तरह से व्यवहार करना" की परिभाषा आपकी आवश्यकताओं पर निर्भर है। एक तकनीक जिसे मैं उपयोग करना पसंद करता हूं वह है "व्यामोह" अपवाद। यदि मुझे 100% यकीन है कि एक निश्चित मामला कभी नहीं हो सकता है, तो मैं अभी भी एक अपवाद कार्यक्रम करता हूं, लेकिन मैं इसे इस तरह से करता हूं कि क) स्पष्ट रूप से सभी को बताता है कि मुझे ऐसा होने की उम्मीद नहीं है, और बी) स्पष्ट रूप से प्रदर्शित और लॉग इन है। इस तरह से बाद में भ्रष्टाचार नहीं बढ़ता।

उदाहरण छद्म कोड:

file = File.open(">", "bla")  or raise "Paranoia: cannot open file 'bla'"

file.write("xytz") or raise "Paranoia: disk full?"

file.close()  or raise "Paranoia: huh?!?!?"

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

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

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


-1

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

सबसे पहले, विधि हैडर:

public static CalendarRow AssignAppointmentToRow(Appointment app, List<CalendarRow> rows)

किस पंक्ति में नियुक्ति दें ? यह पैरामीटर सूची से तुरंत स्पष्ट होना चाहिए। किसी और ज्ञान के बिना, मैं इस तरह से देखने के लिए विधि पारमों की अपेक्षा करूंगा (Appointment app, CalendarRow row):।

अगला, "इनपुट जाँच":

//1. Is rows equal to null? - This will be the case if this is the first appointment.
if (rows == null) {
    rows = new List<CalendarRow> ();
}

//2. Is rows empty? - This will be the case if this is the first appointment / some other unknown reason.
if(rows.Count == 0)
{
    rows.Add (new CalendarRow (0));
    rows [0].Appointments.Add (app);
}

यह बकवास है।

  1. जाँच करें) विधि कॉलर को यह सुनिश्चित करना चाहिए कि यह विधि के अंदर असंबद्ध मूल्यों को पारित नहीं करता है। यह एक प्रोग्रामर की ज़िम्मेदारी है (कोशिश करने के लिए) बेवकूफ नहीं बनना है।
  2. जाँच करें) अगर मैं इस बात को ध्यान में नहीं रखता कि rowsविधि में पास होना शायद गलत है (ऊपर टिप्पणी देखें), तो यह AssignAppointmentToRowकहीं न कहीं नियुक्ति को असाइन करने के अलावा किसी अन्य तरीके से पंक्तियों में हेरफेर करने के लिए कहे जाने वाले तरीके की जिम्मेदारी नहीं होनी चाहिए ।

लेकिन कहीं भी नियुक्तियों को असाइन करने की पूरी अवधारणा अजीब है (जब तक कि यह कोड का GUI हिस्सा नहीं है)। आपके कोड में ऐसा प्रतीत होता है (या कम से कम यह एक कैलेंडर का प्रतिनिधित्व करने वाली एक स्पष्ट डेटा संरचना का प्रयास करता है) (यह है कि List<CalendarRows><- इसे Calendarकहीं के रूप में परिभाषित किया जाना चाहिए यदि आप इस तरह से जाना चाहते हैं, तो आप Calendar calendarअपनी विधि से गुजरेंगे)। यदि आप इस तरह से जाते हैं, तो मैं अपेक्षा करूँगा calendarकि आप स्लॉट्स के साथ प्रीफ़िल्ड होंगे जहाँ आप नियुक्तियाँ (बाद में) नियुक्तियाँ करते हैं (जैसे calendar[month][day] = appointmentउपयुक्त कोड होगा)। लेकिन फिर आप मुख्य तर्क से कैलेंडर संरचना को पूरी तरह से खोदने का विकल्प चुन सकते हैं और List<Appointment>जहां Appointmentऑब्जेक्ट्स में विशेषता होती है, वहां बसdate। और फिर अगर आपको GUI में कहीं कैलेंडर सौंपने की आवश्यकता है, तो आप रेंडर करने से पहले इस 'स्पष्ट कैलेंडर' संरचना को बना सकते हैं।

मुझे आपके ऐप का विवरण नहीं पता है, इसलिए यह संभवत: आपके लिए लागू नहीं है, लेकिन ये दोनों चेक (मुख्य रूप से दूसरा एक) मुझे बताते हैं कि आपके कोड में चिंताओं को अलग करने के साथ कहीं न कहीं कुछ गलत है ।


-2

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

स्यूडोकोड:

let C_now   = cost of implementing the piece of code now
let C_later = ... later
let C_maint = cost of maintaining the piece of code one day
              (note that it can be negative)
let P_need  = probability that you will need the code after N days

if C_now + C_maint * N < P_need*C_later then implement the code else omit it.

इसके लिए कारक C_maint:

  • क्या यह सामान्य रूप से कोड में सुधार करता है, जिससे यह अधिक स्व-दस्तावेजीकरण, परीक्षण करने में आसान होता है? यदि हाँ, तो नकारात्मक की C_maintउम्मीद है
  • क्या यह कोड को बड़ा बनाता है (इसलिए पढ़ना मुश्किल है, संकलन को लागू करना, परीक्षण लागू करना आदि)।
  • क्या कोई रिफ्लेक्टर / रिडिजाइन लंबित हैं? यदि हाँ, टक्कर C_maint। इस मामले में और अधिक जटिल सूत्र की आवश्यकता होती है, जैसे अधिक समय वाले चर N

इतनी बड़ी बात कि कोड को केवल वेट किया जाए और कम संभावना के साथ केवल 2 वर्षों में इसकी आवश्यकता हो सकती है, लेकिन एक छोटी सी बात जो उपयोगी अभिकथनों और 50% को भी ठेस पहुंचाती है, जिसकी आवश्यकता 3 महीने में होगी, इसे लागू किया जाना चाहिए। ।


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