यह इंगित करने के लिए कि हमें त्रुटियां फेंकनी चाहिए, एक ध्वज होने चाहिए


64

मैंने हाल ही में कुछ पुराने डेवलपर्स (लगभग 50+ साल पुराने) के साथ एक जगह पर काम करना शुरू किया। उन्होंने विमानन से निपटने वाले महत्वपूर्ण अनुप्रयोगों पर काम किया है जहां सिस्टम नीचे नहीं जा सका। नतीजतन पुराने प्रोग्रामर इस तरह से कोड करते हैं।

वह वस्तुओं में एक बूलियन डालने के लिए संकेत देता है कि क्या एक अपवाद को फेंक दिया जाना चाहिए या नहीं।

उदाहरण

public class AreaCalculator
{
    AreaCalculator(bool shouldThrowExceptions) { ... }
    CalculateArea(int x, int y)
    {
        if(x < 0 || y < 0)
        {
            if(shouldThrowExceptions) 
                throwException;
            else
                return 0;
        }
    }
}

(हमारी परियोजना में विधि विफल हो सकती है क्योंकि हम एक नेटवर्क डिवाइस का उपयोग करने की कोशिश कर रहे हैं जो उस समय मौजूद नहीं हो सकता है। क्षेत्र का उदाहरण केवल अपवाद ध्वज का एक उदाहरण है)

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

उनका तर्क / तर्क यह है कि हमारे कार्यक्रम को 1 काम करना है, उपयोगकर्ता को डेटा दिखाना है। कोई अन्य अपवाद जो हमें ऐसा करने से नहीं रोकता है उसे अनदेखा किया जाना चाहिए। मैं मानता हूं कि उन्हें नजरअंदाज नहीं किया जाना चाहिए, लेकिन उन्हें उचित व्यक्ति द्वारा नियंत्रित किया जाना चाहिए, और उसके लिए झंडे से निपटना नहीं चाहिए।

क्या यह अपवादों को संभालने का एक अच्छा तरीका है?

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

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


22
c # में इस Try-Parse संरक्षक के लिए एक सम्मेलन है। अधिक जानकारी: docs.microsoft.com/en-us/dotnet/standard/design-guidelines/… झंडा इस पैटर्न के साथ मेल नहीं खाता है।
पीटर

18
यह मूल रूप से एक नियंत्रण पैरामीटर है और यह बदलता है कि कैसे तरीकों इंटर्न निष्पादित करते हैं। यह खराब है, परिदृश्य की परवाह किए बिना। martinfowler.com/bliki/FlagArgument.html , softwareengineering.stackexchange.com/questions/147977/… , medium.com/@amlcurran/…
bic

1
पीटर से ट्राइ-पार्स टिप्पणी के अलावा, यहाँ Vexing अपवादों के बारे में एक अच्छा लेख है: blogs.msdn.microsoft.com/ericlippert/2008/09/10/…
Linaith

2
"इस प्रकार हम एक अपवाद नहीं फेंकना चाहेंगे (और इसे संभाल नहीं सकते?) और क्या यह प्रोग्राम को तब लेगा जब उपयोगकर्ता इसके ठीक काम कर रहा होगा" - आप जानते हैं कि आप अपवादों को सही पकड़ सकते हैं?
user253751

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

जवाबों:


74

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

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

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


1
@ क्वर्क यह प्रभावशाली है कि चेन केवल 3 या 4 लाइनों में सिंगल रिस्पॉन्सिबिलिटी प्रिंसिपल का उल्लंघन करने में कैसे कामयाब रही। यही असली समस्या है। इसके अलावा, वह जिस समस्या के बारे में बात कर रहा है (प्रत्येक पंक्ति पर त्रुटियों के परिणामों के बारे में सोचने में विफल रहने वाला प्रोग्रामर) हमेशा अनियंत्रित अपवादों के साथ एक संभावना है और केवल कभी-कभी जांच किए गए अपवादों के साथ एक संभावना है। मुझे लगता है कि मैंने जाँच किए गए अपवादों के खिलाफ सभी तर्क देखे हैं, और उनमें से एक भी मान्य नहीं है।
TKK

@ टीकेके व्यक्तिगत रूप से कुछ ऐसे मामले हैं जिनमें मैं भाग गया हूं जहां मैं वास्तव में .NET में अपवादों की जांच करना पसंद करता हूं। यह अच्छा होगा यदि कुछ उच्च अंत स्थैतिक विश्लेषण उपकरण थे जो यह सुनिश्चित कर सकते थे कि एपीआई के रूप में इसके फेंके गए अपवाद क्या सटीक हैं, हालांकि यह संभवतः बहुत असंभव होगा, खासकर जब देशी संसाधनों तक पहुंच हो।
जूनियर

1
@ Jrh हाँ, यह अच्छा होगा यदि कुछ .NET में कुछ अपवाद सुरक्षा को हटा दिया जाए, तो टाइपस्क्रिप्ट जेएस के प्रकार की सुरक्षा को कैसे ठीक करता है।
TKK

47

क्या यह अपवादों को संभालने का एक अच्छा तरीका है?

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

सामान्य तौर पर, जब हम कक्षाएं और उनके एपीआई डिजाइन करते हैं, तो हमें उस पर विचार करना चाहिए

  1. एक ही समय में एक ही कार्यक्रम में विभिन्न विन्यासों के साथ कक्षा के कई उदाहरण हो सकते हैं, और,

  2. निर्भरता इंजेक्शन और अन्य प्रोग्रामिंग प्रथाओं की किसी भी संख्या के कारण, एक उपभोग्य ग्राहक वस्तुओं का निर्माण कर सकता है और उन्हें दूसरे उपयोग करने के लिए सौंप सकता है - इसलिए अक्सर हमारे पास ऑब्जेक्ट निर्माता और ऑब्जेक्ट उपयोगकर्ताओं के बीच अलगाव होता है।

अब इस बात पर विचार करें कि एक इंस्टेंस एस का उपयोग करने के लिए विधि कॉलर को क्या करना है / उसे सौंप दिया गया है, उदाहरण के लिए गणना पद्धति को कॉल करने के लिए: कॉलर को शून्य होने के साथ-साथ क्षेत्र को पकड़ने के लिए भी चेक करना होगा - ouch! परीक्षण के विचार न केवल कक्षा के लिए जाते हैं, बल्कि कॉल करने वालों की त्रुटि से भी निपटने के लिए ...

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

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

दो अलग-अलग वर्गों या दो अलग-अलग तरीकों का उपयोग करके, आप आईडीई खोजने की विधि उपयोगकर्ताओं और रिफ्लेक्टर सुविधाओं, आदि का उपयोग कर सकते हैं .. बहुत अधिक आसानी से क्योंकि दो उपयोग के मामले अब भ्रमित नहीं होते हैं। कोड पढ़ना, लिखना, रखरखाव, समीक्षा और परीक्षण सरल है।


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

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


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

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


यह कैसे जारी रखने के लिए फोन करने वाले की जिम्मेदारी नहीं होनी चाहिए?

हाँ मैं सहमत हूँ। ऐसा प्रतीत होता है कि कैली के लिए यह तय करना कि 0 का क्षेत्र त्रुटि स्थितियों के तहत सही उत्तर है (विशेषकर चूंकि 0 एक वैध क्षेत्र है, इसलिए त्रुटि और वास्तविक 0 के बीच अंतर बताने का कोई तरीका नहीं है, हालांकि आपके ऐप पर लागू नहीं हो सकता है)।


आपको वास्तव में अपवाद की जांच करने की आवश्यकता नहीं है क्योंकि आपको विधि को कॉल करने से पहले तर्कों की जांच करनी होगी । शून्य के विरुद्ध परिणाम की जाँच करना कानूनी तर्क 0, 0 और अवैध नकारात्मक लोगों के बीच अंतर नहीं करता है। एपीआई वास्तव में भयानक IMHO है।
ब्लैकजैक

अनुलग्नक K MS C99 और C ++ iostreams के लिए धक्का देता है, एपीआई के उदाहरण हैं जहां एक हुक या झंडा मौलिक रूप से विफलताओं की प्रतिक्रिया को बदल देता है।
डेडुप्लिकेटर

37

उन्होंने विमानन से निपटने वाले महत्वपूर्ण अनुप्रयोगों पर काम किया है जहां सिस्टम नीचे नहीं जा सका। नतीजतन ...

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

  • अपवादों को ठीक से नहीं संभाला जाता है , कम से कम और न ही कठोरता से।

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

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

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

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

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

    CalculateArea(int x, int y, out ErrorCode err)

    तो यह फोन करने वाले के लिए वास्तव में कठिन हो जाता है कि फ़ंक्शन को अनदेखा करने में विफल हो सकता है। लेकिन यह IMHO C # में बेहद बदसूरत है; यह C की एक पुरानी रक्षात्मक प्रोग्रामिंग तकनीक है, जहाँ कोई अपवाद नहीं हैं, और यह आम तौर पर आजकल काम करने के लिए आवश्यक नहीं होना चाहिए।


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

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

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

4
इस। यदि आप प्रोग्राम को क्रैश करने के लिए बर्दाश्त नहीं कर सकते हैं, तो आप इसे चुपचाप गलत जवाब भी नहीं दे सकते। सही उत्तर सभी मामलों में उचित अपवाद से निपटने के लिए है । एक वेबसाइट के लिए, इसका मतलब है कि एक वैश्विक हैंडलर जो अप्रत्याशित त्रुटियों को 500 में बदल देता है। आपके पास अधिक विशिष्ट स्थितियों के लिए अतिरिक्त हैंडलर भी हो सकते हैं, जैसे कि यदि किसी तत्व के विफल होने पर आपको जारी रखने के लिए प्रसंस्करण की आवश्यकता होती है, तो एक लूप के अंदर try/ जैसे catch
jpmc26

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

13

लेखन इकाई परीक्षण थोड़े अधिक जटिल हो जाते हैं क्योंकि आपको हर बार अपवाद ध्वज का परीक्षण करना होता है।

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

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

इसके अलावा, अगर कुछ गलत हो जाता है, तो क्या आप तुरंत जानना नहीं चाहेंगे?

यह आपकी गलत परिभाषा पर निर्भर करता है। प्रश्न में उदाहरण गलत परिभाषित करता है "शून्य से कम का आयाम दिया गया है और shouldThrowExceptionsयह सत्य है।" एक आयाम दिया जा रहा है अगर शून्य से कम गलत नहीं है जब shouldThrowExceptionsगलत है क्योंकि स्विच अलग व्यवहार को प्रेरित करता है। यह काफी सरल बात है, असाधारण स्थिति नहीं है।

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

यह कैसे जारी रखने के लिए फोन करने वाले की जिम्मेदारी नहीं होनी चाहिए?

कॉल करने वाला निर्धारित करता है कि कैसे जारी रखा जाए। इस स्थिति में, यह सेटिंग या समाशोधन द्वारा समय से पहले करता है shouldThrowExceptionsऔर फ़ंक्शन अपने राज्य के अनुसार व्यवहार करता है।

उदाहरण एक विकट रूप से सरल है क्योंकि यह एक एकल गणना और रिटर्न करता है। यदि आप इसे थोड़ा और अधिक जटिल बनाते हैं, जैसे कि संख्याओं की सूची के वर्गमूल की राशि की गणना, अपवादों को फेंकने से कॉलर्स की समस्याएं हो सकती हैं जो वे हल नहीं कर सकते। यदि मैं सूची में पास हो जाता हूं [5, 6, -1, 8, 12]और फ़ंक्शन अपवाद को छोड़ देता है -1, तो मेरे पास फ़ंक्शन को बताते रहने का कोई तरीका नहीं है क्योंकि यह पहले ही निरस्त हो जाएगा और राशि को फेंक देगा। यदि सूची एक बहुत बड़ा डेटा सेट है, तो फ़ंक्शन को कॉल करने से पहले किसी भी नकारात्मक संख्या के बिना प्रतिलिपि बनाना अव्यावहारिक हो सकता है, इसलिए मुझे पहले से यह कहने के लिए मजबूर किया जाता है कि अमान्य नंबरों का इलाज कैसे किया जाना चाहिए, या तो "बस उन्हें अनदेखा करें"। "स्विच या हो सकता है कि एक लैम्ब्डा प्रदान करें जो उस निर्णय को बनाने के लिए बुलाया जाता है।

उनका तर्क / तर्क यह है कि हमारे कार्यक्रम को 1 काम करना है, उपयोगकर्ता को डेटा दिखाना है। कोई अन्य अपवाद जो हमें ऐसा करने से नहीं रोकता है उसे अनदेखा किया जाना चाहिए। मैं मानता हूं कि उन्हें नजरअंदाज नहीं किया जाना चाहिए, लेकिन उन्हें उचित व्यक्ति द्वारा नियंत्रित किया जाना चाहिए, और उसके लिए झंडे से निपटना नहीं चाहिए।

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

और उन बहुत-पुराने प्रोग्रामर में से एक के रूप में, मैं पूछना चाहता हूं कि आप कृपया मेरे लॉन से प्रस्थान करें। ;-)


मैं मानता हूं कि नामकरण और आशय अत्यंत महत्वपूर्ण है और इस मामले में उचित पैरामीटर नाम वास्तव में तालिकाओं को बदल सकता है, इसलिए +1, लेकिन 1. our program needs to do 1 thing, show data to user. Any other exception that doesn't stop us from doing so should be ignoredगलत डेटा के आधार पर उपयोगकर्ता निर्णय लेने की मानसिकता को जन्म दे सकता है (क्योंकि वास्तव में कार्यक्रम को करने की आवश्यकता है 1 बात - सूचित निर्णय लेने में उपयोगकर्ता की मदद करने के लिए) 2. इसी तरह के मामले bool ExecuteJob(bool throwOnError = false)आमतौर पर बेहद त्रुटिपूर्ण होते हैं और कोड के लिए नेतृत्व करते हैं जो कि इसे पढ़कर सिर्फ तर्क करना मुश्किल है।
यूजीन पॉडस्कल

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

अच्छा जवाब, मुझे लगता है कि यह तर्क सिर्फ ओपी के मुकाबले बहुत व्यापक परिस्थितियों में लागू होता है। BTW, cpp के नए में इन कारणों के लिए एक फेंक / नो-थ्रो वर्जन है। मुझे पता है कि कुछ छोटे अंतर हैं लेकिन ...
drjpizzle

8

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

ये अक्सर उन चीजों से संबंधित होते हैं जिनकी आप अपेक्षा करते हैं:

  • Git के लिए, गलत उत्तर बहुत खराब सापेक्ष हो सकता है: टेक-टू-लॉन्ग / एबॉर्टिंग / हैंगिंग या क्रैशिंग (जो प्रभावी रूप से गैर-मुद्दों के सापेक्ष हो, कहे, चेक-इन कोड को गलती से बदल देना)।

    हालाँकि: एक इंस्ट्रूमेंट पैनल के लिए जिसमें जी-फोर्स की गणना रुक रही है और हवा-गति की गणना को अस्वीकार्य होने से रोका जा सकता है।

कुछ कम स्पष्ट हैं:

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

  • राक्षसी रूप से सुरक्षित होना अपेक्षाकृत महत्वपूर्ण है। बहुत से ग्राहक इस बात के लिए नहीं बैठेंगे कि वे जिस स्रोत को खरीद रहे हैं वह सुरक्षित है या नहीं। यदि आप दूसरी ओर विमानन बाजार में हैं ...

यह आपके उदाहरण पर कैसे लागू होता है:

मुझे नहीं पता। ऐसे कई विचार प्रक्रियाएं हैं जिनमें सुरक्षा नियमों में "नो-वन थ्रो इन प्रोडक्शन कोड" जैसे प्रमुख नियम शामिल हो सकते हैं जो सामान्य परिस्थितियों में बहुत मूर्खतापूर्ण होंगे।

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

उस सब ने कहा, यह मुझे एक संदिग्ध लग रहा है लेकिन :

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


7

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

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

कहा जा रहा है कि यह समृद्ध डेटा-प्रकार कार्रवाई को बल नहीं देना चाहिए। अपने क्षेत्र के उदाहरण में कुछ इस तरह की कल्पना करें var area_calc = new AreaCalculator(); var volume = area_calc.CalculateArea(x, y) * z;। लगता है कि उपयोगी volumeक्षेत्र को गहराई से गुणा किया जाना चाहिए - जो एक घन, सिलेंडर, आदि हो सकता है ...

लेकिन क्या होगा अगर area_calc सेवा नीचे थी? फिर area_calc .CalculateArea(x, y)एक त्रुटि के साथ एक समृद्ध डेटाटाइप लौटाया। क्या इसके द्वारा गुणा करना कानूनी है z? यह एक अच्छा सवाल है। आप उपयोगकर्ताओं को तुरंत जाँच करने के लिए बाध्य कर सकते हैं। हालाँकि यह त्रुटि से निपटने के तर्क को तोड़ देता है।

var area_calc = new AreaCalculator();
var area_result = area_calc.CalculateArea(x, y);
if (area_result.bad())
{
    //handle unhappy path
}
var volume = area_result.value() * z;

बनाम

var area_calc = new AreaCalculator();
var volume = area_calc.CalculateArea(x, y) * z;
if (volume.bad())
{
    //handle unhappy path
}

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

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

वैकल्पिक रूप volumeसे सिर्फ एक सादा डेटा प्रकार हो सकता है - सिर्फ एक संख्या, लेकिन फिर त्रुटि स्थिति क्या होती है? यह हो सकता है कि अगर यह एक खुशहाल स्थिति में है, तो इसका अर्थ है कि यह धर्मान्तरित है। क्या यह दुखी स्थिति में होना चाहिए यह एक डिफ़ॉल्ट / त्रुटि मान (क्षेत्र 0 या -1 के लिए उचित लग सकता है) वापस कर सकता है। वैकल्पिक रूप से यह इंटरफ़ेस / भाषा सीमा के इस तरफ एक अपवाद फेंक सकता है।

... foo() {
   var area_calc = new AreaCalculator();
   return area_calc.CalculateArea(x, y) * z;
}
var volume = foo();
if (volume <= 0)
{
    //handle error
}

बनाम

... foo() {
   var area_calc = new AreaCalculator();
   return area_calc.CalculateArea(x, y) * z;
}

try { var volume = foo(); }
catch(...)
{
    //handle error
}

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

बस स्पष्ट होने के लिए एक नाखुश पथ व्यापार तर्क (अपवाद का डोमेन) के लिए एक मामला अज्ञात है, मान्य करने में विफल होना एक खुशहाल रास्ता है क्योंकि आप जानते हैं कि व्यवसाय के नियमों (नियमों के क्षेत्र) से कैसे निपटें।

अंतिम समाधान वह होगा जो सभी परिदृश्यों (कारण के भीतर) की अनुमति देता है।

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

कुछ इस तरह:

Rich::value_type value_or_default(Rich&, Rich::value_type default_value = ...);
bool bad(Rich&);
...unhappy path report... bad_state(Rich&);
Rich& assert_not_bad(Rich&);
class Rich
{
public:
   typedef ... value_type;

   operator value_type() { assert_not_bad(*this); return ...value...; }
   operator X(...) { if (bad(*this)) return ...propagate badness to new value...; /*operate and generate new value*/; }
}

//check
if (bad(x))
{
    var report = bad_state(x);
    //handle error
}

//rethrow
assert_not_bad(x);
var result = (assert_not_bad(x) + 23) / 45;

//propogate
var y = x * 23;

//implicit throw
Rich::value_type val = x;
var val = ((Rich::value_type)x) + 34;
var val2 = static_cast<Rich::value_type>(x) % 3;

//default value
var defaulted = value_or_default(x);
var defaulted_to = value_or_default(x, 55);

@ TobySpeight फेयर काफी, ये चीजें संदर्भ के प्रति संवेदनशील हैं और इनके दायरे की सीमा है।
Kain0_0

मुझे लगता है कि यहाँ समस्या 'assert_not_bad' ब्लॉक है। मुझे लगता है कि ये उसी स्थान पर समाप्त हो जाएंगे जब मूल कोड ने हल करने की कोशिश की थी। परीक्षण में, इन पर ध्यान देने की आवश्यकता है, हालांकि, यदि वे वास्तव में मुखर हैं, तो उन्हें वास्तविक विमान पर उत्पादन से पहले ही हटा दिया जाना चाहिए। अन्यथा कुछ बेहतरीन बाते।
२०:४०

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

3

मैं C ++ के दृष्टिकोण से उत्तर दूंगा। मुझे पूरा यकीन है कि सभी मुख्य अवधारणा C # के लिए हस्तांतरणीय हैं।

ऐसा लगता है कि आपकी पसंदीदा शैली "हमेशा अपवाद फेंकें":

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        throw Exception("negative side lengths");
    }
    return x * y;
}

यह C ++ कोड के लिए एक समस्या हो सकती है क्योंकि अपवाद-हैंडलिंग भारी है - यह विफलता के मामले को धीरे-धीरे चलाता है, और विफलता के मामले को मेमोरी आवंटित करता है (जो कभी-कभी उपलब्ध भी नहीं होता है), और आम तौर पर चीजों को कम अनुमानित करता है। ईएच का भारीपन एक कारण है जो आप लोगों को यह कहते हुए सुनते हैं कि "नियंत्रण प्रवाह के लिए अपवादों का उपयोग न करें।"

तो है (जैसे कि कुछ पुस्तकालयों <filesystem>) का उपयोग क्या सी ++ एक "दोहरी एपीआई," कहते हैं या क्या सी # कॉल Try-Parseपैटर्न (धन्यवाद पीटर टिप के लिए!)

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        throw Exception("negative side lengths");
    }
    return x * y;
}

bool TryCalculateArea(int x, int y, int& result) {
    if (x < 0 || y < 0) {
        return false;
    }
    result = x * y;
    return true;
}

int a1 = CalculateArea(x, y);
int a2;
if (TryCalculateArea(x, y, a2)) {
    // use a2
}

आप "दोहरी APIs" के साथ समस्या को तुरंत देख सकते हैं: बहुत सारे कोड दोहराव, उपयोगकर्ताओं के लिए कोई मार्गदर्शन नहीं, जिसके लिए API का उपयोग करने के लिए "सही" है, और उपयोगकर्ता को उपयोगी त्रुटि संदेशों ( CalculateArea) के बीच एक कठिन विकल्प बनाना चाहिए गति ( TryCalculateArea) क्योंकि तेज संस्करण हमारे उपयोगी "negative side lengths"अपवाद को लेता है और इसे एक बेकार में समतल कर देता है false- "कुछ गलत हो गया, मुझसे मत पूछो कि क्या या कहाँ।" (कुछ दोहरी API, के रूप में एक अधिक अर्थपूर्ण त्रुटि प्रकार का उपयोग int errnoसी ++ के या std::error_codeहै, लेकिन है कि अभी भी आपको बता नहीं है जहां त्रुटि हुई - सिर्फ इतना है कि यह किया था कहीं होते हैं।)

यदि आप यह तय नहीं कर सकते हैं कि आपके कोड को कैसे व्यवहार करना चाहिए, तो आप हमेशा कॉल करने वाले के ऊपर निर्णय को किक कर सकते हैं!

template<class F>
int CalculateArea(int x, int y, F errorCallback) {
    if (x < 0 || y < 0) {
        return errorCallback(x, y, "negative side lengths");
    }
    return x * y;
}

int a1 = CalculateArea(x, y, [](auto...) { return 0; });
int a2 = CalculateArea(x, y, [](int, int, auto msg) { throw Exception(msg); });
int a3 = CalculateArea(x, y, [](int, int, auto) { return x * y; });

यह अनिवार्य रूप से आपके सहकर्मी क्या कर रहा है; सिवाय इसके कि वह "त्रुटि हैंडलर" को एक वैश्विक चर में विभाजित कर रहा है:

std::function<int(const char *)> g_errorCallback;

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        return g_errorCallback("negative side lengths");
    }
    return x * y;
}

g_errorCallback = [](auto) { return 0; };
int a1 = CalculateArea(x, y);
g_errorCallback = [](const char *msg) { throw Exception(msg); };
int a2 = CalculateArea(x, y);

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

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

bool g_errorViaException;

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        return g_errorViaException ? throw Exception("negative side lengths") : 0;
    }
    return x * y;
}

g_errorViaException = false;
int a1 = CalculateArea(x, y);
g_errorViaException = true;
int a2 = CalculateArea(x, y);

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

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

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
#ifdef NEXCEPTIONS
        return 0;
#else
        throw Exception("negative side lengths");
#endif
    }
    return x * y;
}

// Now these two function calls *must* have the same behavior,
// which is a nice property for a program to have.
// Improves understandability.
//
int a1 = CalculateArea(x, y);
int a2 = CalculateArea(x, y);

कुछ इस तरह से काम करता है का एक उदाहरण सी और सी ++ में assertमैक्रो है, जो प्रीप्रोसेसर मैक्रो पर उसके व्यवहार को स्थिति देता है NDEBUG


यदि कोई इसके बजाय std::optionalसे लौटता है TryCalculateArea(), तो दोहरे फ़ंक्शन के दोनों हिस्सों के कार्यान्वयन को एक संकलन-समय-ध्वज के साथ एकल फ़ंक्शन-टेम्पलेट में एकीकृत करना सरल है।
डिडुप्लिकेटर

@ डेडप्लिकेटर: शायद ए के साथ std::expected। बस के साथ std::optional, जब तक मैं आपके प्रस्तावित समाधान को गलत नहीं समझ लेता, तब भी यह मेरे द्वारा कही गई बातों से ग्रस्त होगा: उपयोगकर्ता को उपयोगी त्रुटि संदेशों और गति के बीच एक कठिन चुनाव करना होगा, क्योंकि तेज संस्करण हमारे उपयोगी "negative side lengths"अपवाद को लेता है और इसे बेकार में समतल कर देता है false- " कुछ गलत हो गया है, मुझसे मत पूछो क्या या कहाँ। "
क्क्सप्लसोन

यही कारण है कि libc ++ <filesystem> वास्तव में OP के सहकर्मी पैटर्न के बहुत करीब कुछ करता है: यह std::error_code *ecएपीआई के हर स्तर के नीचे सभी तरह से पाइप करता है, और फिर सबसे नीचे नैतिक के बराबर होता है if (ec == nullptr) throw something; else *ec = some error code। (यह ifकुछ कहा जाता है ErrorHandler, लेकिन यह एक ही मूल विचार है में वास्तविक सार है।)
Quuxplusone

अच्छी तरह से यह एक विकल्प होगा कि विस्तारित त्रुटि-जानकारी को फेंकने के बिना रखा जाए। उचित हो सकता है, या संभावित अतिरिक्त लागत के लायक नहीं हो सकता है।
Deduplicator

1
तो इस जवाब में निहित कई अच्छे विचार ... निश्चित रूप से अधिक upvotes की आवश्यकता है :-)
cmaster

1

मुझे लगता है कि यह उल्लेख किया जाना चाहिए कि आपके सहयोगी को उनका पैटर्न कहां से मिला है।

आजकल, C # में TryGet पैटर्न है public bool TryThing(out result)। इससे आप अपना परिणाम प्राप्त कर सकते हैं, जबकि अभी भी आपको यह बता सकता है कि क्या परिणाम एक वैध मूल्य है। (उदाहरण के लिए, सभी intमानों के लिए मान्य परिणाम हैं Math.sum(int, int), लेकिन यदि मूल्य अतिप्रवाह करने के लिए थे, तो यह विशेष परिणाम कचरा हो सकता है)। यह एक अपेक्षाकृत नया पैटर्न है।

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

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

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


outकीवर्ड से पहले , आप एक boolफ़ंक्शन लिखेंगे जो परिणाम के लिए एक संकेतक लेता है, अर्थात एक refपैरामीटर। आप 1998 में VB6 में ऐसा कर सकते हैं। outकीवर्ड केवल आपको संकलन-समय निश्चितता खरीदता है कि जब फ़ंक्शन वापस आता है, तो पैरामीटर को असाइन किया जाता है, बस यही है। यह है एक अच्छा और उपयोगी पैटर्न हालांकि।
मैथ्यू गुइंडोन

@MathieuGuindon Yeay, लेकिन गेटट्री अभी तक एक अच्छी तरह से ज्ञात / स्थापित पैटर्न नहीं था, और यहां तक ​​कि अगर यह था, तो मुझे पूरी तरह से यकीन नहीं है कि इसका उपयोग किया गया होगा। आखिरकार, Y2K तक लीड का हिस्सा यह था कि 0-99 से बड़ा कुछ भी स्टोर करना अस्वीकार्य था।
तीज

0

ऐसे समय होते हैं जब आप उसका दृष्टिकोण करना चाहते हैं, लेकिन मैं उन्हें "सामान्य" स्थिति नहीं मानूंगा। यह निर्धारित करने की कुंजी कि आप किस मामले में हैं:

उनका तर्क / तर्क यह है कि हमारे कार्यक्रम को 1 काम करना है, उपयोगकर्ता को डेटा दिखाना है। कोई अन्य अपवाद जो हमें ऐसा करने से नहीं रोकता है उसे अनदेखा किया जाना चाहिए।

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

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

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

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


0

इस विशिष्ट उदाहरण में एक दिलचस्प विशेषता है जो नियमों को प्रभावित कर सकती है ...

CalculateArea(int x, int y)
{
    if(x < 0 || y < 0)
    {
        if(shouldThrowExceptions) 
            throwException;
        else
            return 0;
    }
}

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

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

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

// Configurable dependencies
AreaCalculator(PreconditionFailureStrategy strategy)

CalculateArea(int x, int y)
{
    if (x < 0 || y < 0) {
        return this.strategy.fail(0);
    }
    // ...
}

उन्होंने विमानन से निपटने वाले महत्वपूर्ण अनुप्रयोगों पर काम किया है जहां सिस्टम नीचे नहीं जा सका।

राष्ट्रीय यातायात और सुरक्षा बोर्ड वास्तव में अच्छा है; मैं ग्रेबर्ड्स के लिए वैकल्पिक कार्यान्वयन तकनीकों का सुझाव दे सकता हूं, लेकिन मैं त्रुटि रिपोर्टिंग उपप्रणाली में bulkheads डिजाइन करने के बारे में उनसे बहस करने के लिए इच्छुक नहीं हूं।

अधिक व्यापक रूप से: व्यवसाय के लिए लागत क्या है? यह एक जीवन साइट की तुलना में एक वेब साइट को क्रैश करने के लिए बहुत सस्ता है।


मैं अभी भी आधुनिकीकरण करते हुए वांछित लचीलेपन को बनाए रखने के लिए एक वैकल्पिक तरीके के सुझाव को पसंद करता हूं।
drjpizzle

-1

तरीके या तो अपवादों को संभालते हैं या वे नहीं करते हैं, सी # जैसी भाषाओं में झंडे की कोई आवश्यकता नहीं है।

public int Method1()
{
  ...code

 return 0;
}

यदि ... कोड में कुछ खराब हो जाता है, तो उस अपवाद को कॉलर द्वारा नियंत्रित किया जाना चाहिए। यदि कोई त्रुटि को संभालता है, तो कार्यक्रम समाप्त हो जाएगा।

public int Method1()
{
try {  
...code
}
catch {}
 ...Handle error 
}
return 0;
}

इस स्थिति में, यदि कुछ बुरा होता है ... कोड, Method1 समस्या को संभाल रहा है और कार्यक्रम आगे बढ़ना चाहिए।

जहाँ आप अपवादों को संभालते हैं, वह आपके ऊपर है। निश्चित रूप से आप उन्हें पकड़ने और कुछ नहीं करने से अनदेखा कर सकते हैं। लेकिन, मैं यह सुनिश्चित करूंगा कि आप केवल कुछ विशिष्ट प्रकार के अपवादों को अनदेखा कर रहे हैं जो आप होने की उम्मीद कर सकते हैं। इग्नोर करना ( exception ex) खतरनाक है क्योंकि कुछ अपवाद आप स्मृति से बाहर के सिस्टम अपवादों की तरह अनदेखा नहीं करना चाहते हैं और ऐसे।


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

-1

यह दृष्टिकोण "असफल तेज़, असफल कठिन" दर्शन को तोड़ता है।

आप तेजी से असफल क्यों होना चाहते हैं:

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

तेज और कठिन असफल होने की कमियां :

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

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

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

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

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

एक टिप्पणी में आने वाला एक बिंदु:

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

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

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


3
यह अन्य उत्तरों में बताया गया है कि जब आप जिस हार्डवेयर पर चल रहे होते हैं वह एक हवाई जहाज होता है, तो एक महत्वपूर्ण एप्लिकेशन में तेज और कठिन असफल होना वांछनीय नहीं है।
14

1
@ फिर, यह एक गलतफहमी है जो तेजी से और कठिन साधनों को विफल कर रही है। मैंने उत्तर के बारे में इस बारे में एक पैराग्राफ जोड़ा है।
AnoE

भले ही इरादा 'उस कठिन' को विफल करने का नहीं है, फिर भी उस तरह की आग से खेलना जोखिम भरा है।
drjpizzle

इस तरह की बात है, @drjpizzle। यदि आप कठिन-असफल उपवास को विफल करने के लिए उपयोग किए जाते हैं, तो यह मेरे अनुभव में "जोखिम भरा" या "उस तरह की आग से खेलना" नहीं है। Au contraire। इसका मतलब है कि आपको यह सोचने की आदत है कि "क्या होगा अगर मुझे यहां कोई अपवाद मिला है", जहां "यहां" का अर्थ हर जगह है , और आप जानते हैं कि क्या आप जिस स्थान पर वर्तमान में प्रोग्रामिंग कर रहे हैं, वह एक बड़ी समस्या है ( विमान दुर्घटना, जो भी उस मामले में)। आग से खेलना सब कुछ ठीक होने की उम्मीद करना, और हर घटक, संदेह में, यह
दिखाते
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.