असफल होने पर अपवाद / सत्य बनाम झूठे बनाम वापसी का कार्य और अपवाद को फेंकना


22

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

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

क्या यह एक अच्छा अभ्यास है या यह एक वस्तु को वापस करना बेहतर है (एक बूलियन के साथ, एक वैकल्पिक त्रुटि संदेश और त्रुटियों के लिए एनम)?



59
सच्चा और झूठा साथ-साथ चलता है। शून्य और अपवाद एक साथ अच्छी तरह से चलते हैं। सच और अपवाद बिल्लियों और करों की तरह एक साथ चलते हैं। किसी दिन मैं आपका कोड पढ़ सकता हूं। कृपया मुझे यह मत करो।
कैंडिड_ऑरेंज सेप

4
मुझे ऐसे पैटर्न पसंद हैं जहां एक वस्तु वापस आ जाती है जो सफलता या विफलता दोनों को मिला देती है। उदाहरण के लिए, रूस्ट में सफल होने पर परिणाम Result<T, E>कहां Tहै और Eअसफल होने पर त्रुटि है। एक अपवाद को फेंकना (- जावा में इतना सुनिश्चित नहीं हो सकता है) महंगा हो सकता है क्योंकि इसमें कॉल स्टैक को खोलना शामिल है, लेकिन एक अपवाद ऑब्जेक्ट बनाना सस्ता है। जाहिर है, आपके द्वारा उपयोग की जा रही भाषा के स्थापित पैटर्न से चिपके रहें, लेकिन इस तरह से बूलियन्स और अपवादों को संयोजित न करें।
डैन पेंट्री

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

4
@DanPantry जावा में, कॉल स्टैक को अपवाद बनाते समय चेक किया जाता है , न कि इसे फेंकते समय। fillInStackTrace();सुपर कंस्ट्रक्टर में कहा जाता है, कक्षा मेंThrowable
साइमन फोर्सबर्ग

जवाबों:


35

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

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

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


7
यदि यह किसी फ़ाइल अपलोड अनुरोध के विफल होने के लिए अपेक्षित परिणाम है, तो मुझे अपने चेहरे पर एक अपवाद द्वारा आश्चर्य होगा (विशेषकर यदि विधि हस्ताक्षर में वापसी मूल्य है)।
हॉफमैले

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

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

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

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

31

trueयदि आप falseअसफलता पर नहीं लौटते हैं तो सफलता पर लौटने का कोई कारण नहीं है । क्लाइंट कोड कैसा दिखना चाहिए?

if (result = tryMyAPICall()) {
    // business logic
}
else {
    // this will *never* happen anyways
}

इस मामले में, कॉल करने वाले को किसी भी तरह से ब्लॉक-कैच करने की आवश्यकता होती है, लेकिन फिर वह बेहतर लिख सकता है:

try {
    result = tryMyAPICall();
    // business logic
    // will only reach this line when no exception
    // no reason to use an if-condition
} catch (SomeException se) { }

इसलिए trueकॉलर के लिए रिटर्न वैल्यू पूरी तरह से irelevant है। तो बस विधि रखिए void


सामान्य तौर पर, फेलियर मोड डिजाइन करने के तीन तरीके हैं।

  1. सच्चा / झूठा लौटें
  2. उपयोग void, फेंक (जाँच) अपवाद
  3. एक मध्यवर्ती परिणाम वस्तु लौटाएं ।

वापसी true/false

यह कुछ पुराने, ज्यादातर सी-स्टाइल एपीआई में उपयोग किया जाता है। Downsides obviuos हैं, आपको पता नहीं है कि क्या गलत हुआ। PHP यह अक्सर करता है, इस तरह कोड के लिए अग्रणी:

if (xyz_parse($data) === FALSE)
   $error = xyz_last_error();

बहु-थ्रेडेड संदर्भों में, यह और भी खराब है।

अपवादों को फेंका (चेक किया गया)

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

परिणाम वस्तु वापस करें

इसे संभालने का यह एक और अच्छा तरीका है। इसका उपयोग अक्सर पार्सिंग या केवल उन चीजों के लिए किया जाता है जिन्हें मान्य करने की आवश्यकता होती है।

ValidationResult result = parser.validate(data);
if (result.isValid())
    // business logic
else
    error = result.getvalidationError();

कॉल करने वाले के लिए भी अच्छा, स्वच्छ तर्क।

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

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


5

यह वास्तव में नीचे आता है कि क्या एक विफलता असाधारण या अपेक्षित है

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

दूसरी ओर त्रुटियों पर आम हैं, तो आप डेवलपर हैं जो उनके लिए जाँच कर रहा है के लिए अनुकूलन करना चाहिए, और try-catchखंड में थोड़ा और अधिक एक से बोझिल हैं if/elifया switchश्रृंखला।

हमेशा किसी व्यक्ति की मानसिकता में एक एपीआई डिज़ाइन करें जो इसका उपयोग कर रहा है।


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

4

यदि आप कभी लौटने का इरादा नहीं करते हैं तो एक बूलियन वापस न करें false। बस विधि voidऔर दस्तावेज़ बनाएं कि यह IOExceptionविफलता पर एक अपवाद ( उपयुक्त है) फेंक देगा ।

इसका कारण यह है कि यदि आप एक बूलियन वापस करते हैं, तो आपके एपीआई का उपयोगकर्ता यह निष्कर्ष निकाल सकता है कि वह ऐसा कर सकता है:

if (fileUpload(file) == false) {
    // Handle failure.
}

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

try {
    fileUpload(file);
} catch (IOException e) {
    // Handle failure.
}

3

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

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

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


1
त्रुटि संदेश को पार्स किए बिना विभिन्न प्रकार की त्रुटियों को संभालने के लिए एपीआई उपयोगकर्ता की मदद करने के लिए एनम केवल एक विचार था। इसलिए मेरे मामले में आपके दृष्टिकोण से, एनम को वापस करना बेहतर है और "ओके" के लिए एक एनम जोड़ें।
अकोलातिवो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.