मुझे बताया गया है कि अपवाद केवल असाधारण मामलों में उपयोग किए जाने चाहिए। मुझे कैसे पता चलेगा कि मेरा मामला असाधारण है?


99

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

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

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

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

वैसे भी, मैंने यहाँ जो कुछ भी कहा है, उसे ठीक करने के लिए स्वतंत्र महसूस करें। मेरा मुख्य प्रश्न यह है कि अगर कोई कहता है कि अपवाद असाधारण होना चाहिए, तो मुझे कैसे पता चलेगा कि मेरा मामला असाधारण है?


3
संभव डुप्लिकेट? अपवाद कब फेंकना है । हालांकि यह वहां बंद था, लेकिन मुझे लगता है कि यह यहां फिट बैठता है। यह अभी भी थोड़ा सा दर्शन है, कुछ लोग और समुदाय अपवादों को एक प्रकार के प्रवाह नियंत्रण के रूप में देखते हैं।
थोरस्टेन मुलर

8
जब उपयोगकर्ता गूंगे होते हैं, तो वे अमान्य इनपुट प्रदान करते हैं। जब उपयोगकर्ता स्मार्ट होते हैं, तो वे अमान्य इनपुट प्रदान करके खेलते हैं। इसलिए, अमान्य उपयोगकर्ता इनपुट अपवाद नहीं है।
मौविइल

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

4
"असाधारण"! = "दुर्लभ रूप से होता है"
सशर्त

3
मुझे लगता है कि एरिक लिपर्ट की वैक्सिंग अपवादों को अच्छी सलाह है।
ब्रायन

जवाबों:


87

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

विशेष रूप से जावा में, जो शायद अब तक की सबसे अपवाद-प्रिय भाषा है, आपने कभी भी अपने कोड को सरल बनाने पर अपवादों का उपयोग करने के बारे में बुरा नहीं सोचा होगा वास्तव में, जावा की अपनी Integerकक्षा में यह जांचने का साधन नहीं है कि क्या कोई स्ट्रिंग संभावित रूप से फेंकने के बिना एक वैध पूर्णांक है NumberFormatException

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


10
अच्छा स्वाइप, वहाँ। वास्तव में, मैं तैयार किया गया वास्तविक दुनिया अनुप्रयोग में, प्रदर्शन हिट किया था एक फर्क है, और मैं इसे बदलने के लिए किया था नहीं कुछ पार्स कार्यों के लिए अपवाद फेंक देते हैं।
रॉबर्ट हार्वे

17
मैं यह नहीं कह रहा हूं कि अभी भी ऐसे मामले नहीं हैं जहां प्रदर्शन हिट एक वैध कारण है, लेकिन वे मामले नियम के बजाय अपवाद (दंडनीय उद्देश्य) हैं।
कार्ल बेज़ेलफेल्ट

11
@RobertHarvey Java में ट्रिक प्री-फैब्रिकेटेड अपवाद ऑब्जेक्ट्स को फेंकने के बजाय है throw new ...। या, कस्टम अपवाद फेंकें, जहां fillInStackTrace () ओवरराइट किया गया है। तब आपको किसी भी प्रदर्शन में गिरावट नहीं दिखनी चाहिए, न कि हिट्स की बात करने के लिए ।
इंगो

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

4
@ ब्रेंडन मान लीजिए कि कुछ अपवाद होते हैं, और कॉल-स्टैक में त्रुटि-हैंडलिंग कोड 4 स्तर नीचे है। यदि आप एक त्रुटि कोड का उपयोग करते हैं, तो हैंडलर के ऊपर के सभी 4 कार्यों में त्रुटि कोड का प्रकार उनकी वापसी मान के रूप में होना चाहिए, और आपको if (foo() == ERROR) { return ERROR; } else { // continue }प्रत्येक स्तर पर एक श्रृंखला करनी होगी । यदि आप एक अनियंत्रित अपवाद को फेंकते हैं, तो कोई शोर और अतिरेक नहीं होता है "यदि त्रुटि वापस आती है"। साथ ही, यदि आप फ़ंक्शन को तर्क के रूप में पारित कर रहे हैं, तो त्रुटि कोड का उपयोग करके फ़ंक्शन हस्ताक्षर को असंगत प्रकार में बदल सकते हैं, भले ही त्रुटि हो सकती है।
डोभाल

72

एक अपवाद कब फेंका जाना चाहिए? जब यह कोड की बात आती है, तो मुझे लगता है कि निम्नलिखित स्पष्टीकरण बहुत उपयोगी है:

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

यह क्यों सहायक है? यह बताता है कि यह उस संदर्भ पर निर्भर करता है जब कुछ को अपवाद के रूप में संभाला जाना चाहिए या नहीं। विधि कॉल के स्तर पर, संदर्भ (ए) विधि के नाम, (बी) हस्ताक्षर और (बी) ग्राहक कोड का उपयोग करता है, जो विधि का उपयोग करता है या होने की उम्मीद है।

आपके प्रश्न का उत्तर देने के लिए आपके पास कोड पर एक नज़र होनी चाहिए, जहाँ उपयोगकर्ता इनपुट संसाधित होता है। यह कुछ इस तरह लग सकता है:

public void Save(PersonData personData) {  }

क्या विधि का नाम बताता है कि कुछ सत्यापन किया गया है? नहीं। इस मामले में एक अमान्य व्यक्तिडता को एक अपवाद फेंकना चाहिए।

मान लीजिए कि कक्षा में एक और तरीका है जो इस तरह दिखता है:

public ValidationResult Validate(PersonData personData) {  }

क्या विधि का नाम बताता है कि कुछ सत्यापन किया गया है? हाँ। इस मामले में एक अमान्य व्यक्तिडता को एक अपवाद नहीं फेंकना चाहिए।

चीजों को एक साथ रखने के लिए, दोनों विधियों का सुझाव है कि ग्राहक कोड इस तरह दिखना चाहिए:

ValidationResult validationResult = personRegister.Validate(personData);
if (validationResult.IsValid())
{
    personRegister.Save(personData)
}
else
{
    // Throw an exception? To answer this look at the context!
    // That is: (a) Method name, (b) signature and
    // (c) where this method is (expected) to be used.
}

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


कल ही मैंने एक struct"ValidationResult" बनाया और मेरे कोड को आपके द्वारा बताए गए तरीके से संरचित किया।
पउल

4
यह आपके प्रश्न का उत्तर देने में मदद नहीं करता है, लेकिन मैं केवल यह बताना चाहता हूं कि आपने कमांड-क्वेरी पृथक्करण सिद्धांत ( en.wikipedia.org/wiki/Command-query_separation ) का संक्षिप्त या जानबूझकर अनुसरण किया है । ;-)
थियो लेनडॉर्फ

अछा सुझाव! एक खामी: आपके उदाहरण में, सत्यापन वास्तव में दो बार किया जाता है: एक बार के दौरान Validate(यदि अमान्य है तो गलत लौटा) और एक बार के दौरान Save(एक विशिष्ट, अच्छी तरह से प्रलेखित अपवाद को फेंक दिया जाता है)। बेशक, सत्यापन परिणाम को ऑब्जेक्ट के अंदर कैश किया जा सकता है, लेकिन यह अतिरिक्त जटिलता को जोड़ देगा, क्योंकि सत्यापन परिणाम को परिवर्तनों पर अमान्य करने की आवश्यकता होगी।
हेनजी

@ हिनजी मैं सहमत हूँ। इसे फिर से सक्रिय किया जा सकता है, इसलिए Validate()इसे Save()विधि के अंदर कहा जाता है , और ValidationResultअपवाद के लिए एक उपयुक्त संदेश का निर्माण करने के लिए विशिष्ट विवरण का उपयोग किया जा सकता है।
फिल

3
यह मेरे विचार से स्वीकृत उत्तर से बेहतर है। जब कॉल उस चीज़ को नहीं कर सकता जो वह करना चाहता था।
एंडी

31

अपवाद असाधारण होना चाहिए: यह अपेक्षा की जाती है कि उपयोगकर्ता अमान्य डेटा इनपुट कर सकता है, इसलिए यह एक असाधारण मामला नहीं है

उस तर्क पर:

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

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

इसलिए मुझे लगता है कि "अपवाद असाधारण होना चाहिए" अंगूठे का एक भयानक नियम है।

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

मैं पायथन के दृष्टिकोण को पसंद करता हूं, लेकिन मुझे लगता है कि अन्य भाषाओं को इसमें शामिल करना एक बुरा विचार है।


1
@ पता है, मुझे पता है। मेरा कहना था कि आपको भाषा (इस मामले में जावा) के सम्मेलनों का पालन करना चाहिए, भले ही वे आपके पसंदीदा न हों।
विंस्टन एवर्ट

6
+1 "exceptions should be exceptional" is a terrible rule of thumb.खैर ने कहा! यह उन चीजों में से एक है जिन्हें लोग उनके बारे में सोचे बिना दोहराते हैं।
एंड्रेस एफ।

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

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

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

30

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

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

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

EDIT (विचार करने के लिए कुछ और):

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

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


8
पर क्यों? ऐसी समस्याएं कम होने में अपवाद क्यों उपयोगी हैं जो अधिक अपेक्षित हैं?
विंस्टन एवर्ट

6
@Petetree, फ़ाइल खोलने से पहले फ़ाइल के अस्तित्व की जाँच करना बुरा विचार है। फ़ाइल चेक और ओपन के बीच मौजूद नहीं रह सकती है, फ़ाइल की अनुमति नहीं हो सकती है कि आप इसे खोलें, और अस्तित्व की जाँच करें और फिर फ़ाइल को खोलने के लिए दो महंगी सिस्टम कॉल की आवश्यकता होगी। आप बेहतर तरीके से फ़ाइल खोलने की कोशिश कर रहे हैं और फिर ऐसा करने में विफलता से निपट रहे हैं।
विंस्टन एवर्ट

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

11
मैं आपके आधार से असहमत हूं कि क्योंकि अमान्य उपयोगकर्ता डेटा अपेक्षित है, इसे असाधारण नहीं माना जा सकता। अगर मैं एक पार्सर लिखता हूं, और कोई इसे अप्राप्य डेटा खिलाता है, तो यह एक अपवाद है। मैं पार्सिंग जारी नहीं रख सकता। अपवाद को कैसे नियंत्रित किया जाता है यह पूरी तरह से एक और सवाल है।
ConditionRacer

4
File.ReadAllBytes FileNotFoundExceptionगलत इनपुट दिए जाने पर फेंक देगा (उदाहरण के लिए एक गैर-मौजूद फ़ाइल नाम)। इस त्रुटि को खतरे में डालने का एकमात्र वैध तरीका है, त्रुटि कोड वापस करने के परिणामस्वरूप आप और क्या कर सकते हैं?
o

16

संदर्भ

से व्यावहारिक प्रोग्रामर:

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

वे पढ़ने के लिए एक फ़ाइल खोलने के उदाहरण की जांच करने के लिए आगे बढ़ते हैं, और फ़ाइल मौजूद नहीं है - क्या इसका अपवाद उठाना चाहिए?

यदि फ़ाइल होनी चाहिए थी, तो एक अपवाद वारंट है। [...] दूसरी ओर, अगर आपको पता नहीं है कि फ़ाइल मौजूद होनी चाहिए या नहीं, तो यह असाधारण नहीं लगता है यदि आप इसे नहीं ढूंढ सकते हैं, और एक त्रुटि वापसी उचित है।

बाद में, उन्होंने चर्चा की कि उन्होंने इस दृष्टिकोण को क्यों चुना:

[ए] एन अपवाद नियंत्रण का एक तत्काल, गैर-स्थानांतरण हस्तांतरण का प्रतिनिधित्व करता है - यह एक प्रकार का कैस्केडिंग है goto। प्रोग्राम जो अपने सामान्य प्रसंस्करण के हिस्से के रूप में अपवादों का उपयोग करते हैं, वे क्लासिक स्पेगेटी कोड की सभी पठनीयता और स्थिरता की समस्याओं से ग्रस्त हैं। ये प्रोग्राम इनकैप्सुलेशन को तोड़ते हैं: रूटीन और उनके कॉलर अपवाद हैंडलिंग के माध्यम से अधिक कसकर युग्मित होते हैं।

अपनी स्थिति के बारे में

आपका प्रश्न "क्या सत्यापन त्रुटियों में अपवादों को उठाना चाहिए?" जवाब यह है कि यह निर्भर करता है कि सत्यापन कहां हो रहा है।

यदि प्रश्न में विधि कोड के एक खंड के भीतर है जहां यह माना जाता है कि इनपुट डेटा पहले से ही मान्य है, तो अमान्य इनपुट डेटा को एक अपवाद उठाना चाहिए; यदि कोड इस तरह डिज़ाइन किया गया है कि यह विधि उपयोगकर्ता द्वारा दर्ज किए गए सटीक इनपुट को प्राप्त करेगी, तो अमान्य डेटा की उम्मीद की जानी चाहिए, और एक अपवाद नहीं उठाया जाना चाहिए।


11

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

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


1
+1, मैं जो कहने जा रहा था, उसके बहुत करीब। मैं कहूंगा कि यह गुंजाइश के बारे में अधिक है, और वास्तव में उपयोगकर्ता के साथ कुछ भी नहीं करना है। इसका एक अच्छा उदाहरण दोनों के बीच अंतर है। नेट फ़ंक्शन int.Parse और int.TryParse, पूर्व के पास खराब इनपुट पर एक अपवाद फेंकने के अलावा कोई विकल्प नहीं है, बाद में कभी भी एक अपवाद नहीं फेंकना चाहिए
jmoreno

1
@jmoreno: Ergo, आप TryParse का उपयोग करेंगे जब कोड अप्रभावी स्थिति के बारे में कुछ कर सकता है, और Parse जब यह नहीं हो सकता।
रॉबर्ट हार्वे

7

दो चिंताओं पर आपको विचार करना चाहिए:

  1. आप एक ही चिंता पर चर्चा करते हैं - चलो इसे कॉल करें Assignerक्योंकि यह चिंता संरचित वस्तुओं को इनपुट आवंटित करने के लिए है - और आप उन बाधाओं को व्यक्त करते हैं जो इनपुट मान्य हैं

  2. एक अच्छी तरह से कार्यान्वित उपयोगकर्ता-इंटरफ़ेस में एक अतिरिक्त चिंता है: त्रुटियों पर उपयोगकर्ता इनपुट और रचनात्मक प्रतिक्रिया का सत्यापन (चलो इस भाग को कॉल करें Validator)

Assignerघटक के दृष्टिकोण से , एक अपवाद फेंकना पूरी तरह से उचित है, क्योंकि आपने एक बाधा व्यक्त की है जिसका उल्लंघन किया गया है।

उपयोगकर्ता अनुभव के दृष्टिकोण से, उपयोगकर्ताAssigner को पहली जगह में सीधे इस बारे में बात नहीं करनी चाहिए । वे इसे करने के लिए बात कर रही किया जाना चाहिए के माध्यम सेValidator

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

आप देखेंगे कि मैंने यह उल्लेख नहीं किया कि ये चिंताएँ कैसे लागू होती हैं। ऐसा लगता है कि आप के बारे में बात कर रहे हैं Assignerऔर आपके सहयोगी एक संयुक्त के बारे में बात कर रहे हैं Validator+Assigner। एक बार जब आप महसूस करते हैं कि दो अलग (या अलग-अलग) चिंताएं हैं, तो कम से कम आप इसे समझदारी से चर्चा कर सकते हैं।


रेनान की टिप्पणी को संबोधित करने के लिए, मैं सिर्फ यह मान रहा हूं कि एक बार आपने अपनी दो अलग-अलग चिंताओं को पहचान लिया है, यह स्पष्ट है कि प्रत्येक संदर्भ में किन मामलों को असाधारण माना जाना चाहिए।

वास्तव में, यदि यह स्पष्ट नहीं है कि क्या कुछ को असाधारण माना जाना चाहिए, तो मेरा तर्क है कि आपने अपने समाधान में स्वतंत्र चिंताओं की पहचान नहीं की है।

मुझे लगता है कि इसका सीधा जवाब देता है

... मुझे कैसे पता चलेगा कि मेरा मामला असाधारण है?

जब तक यह स्पष्ट न हो, सरलीकृत रखें । जब आपके पास सरल अवधारणाओं का एक ढेर होता है जिसे आप अच्छी तरह से समझते हैं, तो आप उन्हें कोड, कक्षाओं, पुस्तकालयों या जो कुछ भी वापस करने के बारे में स्पष्ट रूप से समझ सकते हैं।


-1 हां, दो चिंताएं हैं, लेकिन यह इस सवाल का जवाब नहीं देता है "मुझे कैसे पता चलेगा कि मेरा मामला असाधारण है?"
आरएमलके

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

... वास्तव में, शायद यह नहीं है - मैंने आपके जवाब में अपनी बात कह दी है, इसके बजाय।
बेकार

4

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


3

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

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

एक सामान्य नियम के रूप में, यदि:

  • आपका कोड बग मुक्त है, और
  • जिन सेवाओं पर यह निर्भर करता है वे सभी उपलब्ध हैं, और
  • आपका उपयोगकर्ता आपके प्रोग्राम का उपयोग उस तरीके से कर रहा है, जिसका उपयोग करने का इरादा था (भले ही उनके द्वारा प्रदान किए गए कुछ इनपुट अमान्य हैं)

तब आपके कोड को कभी भी अपवाद नहीं फेंकना चाहिए, यहां तक ​​कि बाद में पकड़ा गया। अमान्य डेटा को फंसाने के लिए, आप यूआई स्तर पर सत्यापनकर्ताओं का उपयोग कर सकते हैं या Int32.TryParse()प्रस्तुति परत में जैसे कोड ।

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


2

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

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

temp = dataSource.readInteger();
if (temp == null) return null;
field1 = (int)temp;
temp = dataSource.readInteger();
if (temp == null) return null;
field2 = (int)temp;
temp = dataSource.readString();
if (temp == null) return null;
field3 = temp;

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

field1 = dataSource.readInteger();
field2 = dataSource.readInteger();
field3 = dataSource.readString();

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

do
{
  temp = dataSource.tryReadInteger();
  if (temp == null) break;
  total += (int)temp;
} while(true);

बनाम

try
{
  do
  {
    total += (int)dataSource.readInteger();
  }
  while(true);
}
catch endOfDataSourceException ex
{ // Don't do anything, since this is an expected condition (eventually)
}

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

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


2

सॉफ्टवेयर लिखने में सबसे महत्वपूर्ण बात यह पठनीय है। इसे प्रभावी बनाने और इसे सही बनाने सहित अन्य सभी विचार गौण हैं। यदि यह पठनीय है, तो बाकी को रखरखाव में ध्यान रखा जा सकता है, और यदि यह पठनीय नहीं है, तो आप इसे फेंकने से बेहतर हैं। इसलिए, आपको अपवाद को फेंक देना चाहिए जब यह पठनीयता बढ़ाता है।

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

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

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


2

हर त्रुटि पर रिपोर्ट करने की आवश्यकता हो सकती है जो हम इनपुट में पा सकते हैं, न कि केवल पहले।

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

एक बुरा उदाहरण:

Dogअपवादों का उपयोग करते हुए कक्षा के लिए सत्यापन विधि :

void validate(Set<DogValidationException> previousExceptions) {
    if (!DOG_NAME_PATTERN.matcher(this.name).matches()) {
        DogValidationException disallowedName = new DogValidationException(Problem.DISALLOWED_DOG_NAME);
        if (!previousExceptions.contains(disallowedName)){
            throw disallowedName;
        }
    }
    if (this.legs < 4) {
        DogValidationException invalidDog = new DogValidationException(Problem.LITERALLY_INVALID_DOG);
        if (!previousExceptions.contains(invalidDog)){
            throw invalidDog;
        }
    }
    // etc.
}

इसे कैसे कॉल करें:

Set<DogValidationException> exceptions = new HashSet<DogValidationException>();
boolean retry;
do {
    retry = false;
    try {
        dog.validate(exceptions);
    } catch (DogValidationException e) {
        exceptions.add(e);
        retry = true;
    }
} while (retry);

if(exceptions.isEmpty()) {
    dogDAO.beginTransaction();
    dogDAO.save(dog);
    dogDAO.commitAndCloseTransaction();
} else {
    // notify user to fix the problems
}

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

बेहतर तरीका है:

विधि कॉल:

Set<Problem> validationResults = dog.validate();
if(validationResults.isEmpty()) {
    dogDAO.beginTransaction();
    dogDAO.save(dog);
    dogDAO.commitAndCloseTransaction();
} else {
    // notify user to fix the problems
}

सत्यापन विधि:

Set<Problem> validate() {
    Set<Problem> result = new HashSet<Problem>();
    if(!DOG_NAME_PATTERN.matcher(this.name).matches()) {
        result.add(Problem.DISALLOWED_DOG_NAME);
    }
    if(this.legs < 4) {
        result.add(Problem.LITERALLY_INVALID_DOG);
    }
    // etc.
    return result;
}

क्यों? कई कारण हैं, और अन्य प्रतिक्रियाओं में सबसे अधिक कारणों को इंगित किया गया है। इसे सरल करने के लिए: दूसरों द्वारा पढ़ना और समझना बहुत सरल है। दूसरा, क्या आप उपयोगकर्ता को यह बताना चाहते हैं कि वह उसे dogगलत तरीके से सेट करता है ?

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.