त्रुटि चर एक विरोधी पैटर्न या अच्छे डिजाइन हैं?


44

निष्पादन को रोकने के लिए कई संभावित त्रुटियों को संभालने के लिए, मेरे पास एक errorचर है जिसे ग्राहक अपवादों को फेंकने के लिए जांच और उपयोग कर सकते हैं। क्या यह एंटी-पैटर्न है? क्या इससे निपटने का एक बेहतर तरीका है? इस क्रिया में एक उदाहरण के लिए आप PHP की mysqli API देख सकते हैं । मान लें कि दृश्यता समस्याएं (एक्सेसर्स, सार्वजनिक और निजी दायरे, एक वर्ग या वैश्विक में चर है?) को सही तरीके से संभाला जाता है।


6
यह क्या try/ के लिए catchमौजूद है। इसके अतिरिक्त, आप इसे संभालने के लिए एक अधिक उपयुक्त स्थान पर अपने try/ catchबहुत आगे रख सकते हैं (चिंताओं के अधिक अलगाव के लिए अनुमति देता है)।
jpmc26

कुछ बातों को ध्यान में रखें: यदि आप अपवाद-आधारित हैंडलिंग का उपयोग करने जा रहे हैं और आपको एक अपवाद मिलता है, तो आप उपयोगकर्ता को बहुत अधिक जानकारी नहीं दिखाना चाहते हैं। इसे इंटरसेप्ट करने के लिए Elmah या Raygun.io जैसे एरर हैंडलर का उपयोग करें और उपयोगकर्ता को एक जेनेरिक एरर मैसेज दिखाएं। उपयोगकर्ता को स्टैक ट्रेस या एक विशिष्ट त्रुटि संदेश दिखाएं, क्योंकि वे ऐप के आंतरिक कामकाज के बारे में जानकारी का खुलासा करते हैं, जिसका दुरुपयोग किया जा सकता है।
नजल्ल

4
@ आपकी सलाह केवल सुरक्षा-महत्वपूर्ण अनुप्रयोगों के लिए लागू होती है जहाँ उपयोगकर्ता पूरी तरह से अविश्वासित है। अस्पष्ट त्रुटि संदेश स्वयं एक प्रतिमान हैं। तो उपयोगकर्ता की सहमति के बिना नेटवर्क पर त्रुटि रिपोर्ट भेज रहा है।
14 अक्टूबर

3
@piedar मैंने एक अलग प्रश्न बनाया है जहाँ इस पर अधिक स्वतंत्र रूप से चर्चा की जा सकती है: programmers.stackexchange.com/questions/245255/…
Nzall

26
एपीआई डिजाइन में एक सामान्य सिद्धांत जो आपको काफी दूर ले जाता है वह हमेशा यह देखता है कि पीएचपी क्या कर रहा है, और फिर सटीक विपरीत काम कर रहा है।
फिलिप

जवाबों:


65

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

यदि आपके पास कोई विकल्प है, तो अपवादों का उपयोग करने के लिए कुछ फायदे हैं।

संदेश

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

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

अपवाद संदेशों के मामले में, मैं यथासंभव अधिक संदर्भ प्रदान करता हूं, जैसे:

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

इसकी तुलना रिटर्न कोड -85 से करें। आप किसे पसंद करेंगे?

ढेर को बुलाओ

अपवादों में आमतौर पर विस्तृत कॉल स्टैक होते हैं जो डिबग कोड को तेज़ी से और तेज़ी से मदद करते हैं, और यदि वांछित हो तो कॉलिंग कोड द्वारा भी लॉग इन किया जा सकता है। यह डेवलपर्स को आमतौर पर सटीक लाइन पर समस्या को इंगित करने की अनुमति देता है, और इस प्रकार बहुत शक्तिशाली है। एक बार फिर, इसकी तुलना रिटर्न मानों (जैसे -85, 101, 0, आदि) के साथ एक लॉग फ़ाइल में करें, आप किसे पसंद करेंगे?

तेजी से पक्षपाती दृष्टिकोण विफल

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

रैपिंग और अनवांटेड एक्सेप्शन

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

कोड की सादगी

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

निष्कर्ष

यदि आप जिस प्लेटफ़ॉर्म का उपयोग कर रहे हैं वह अपवादों का उपयोग कर रहा है (जैसे कि जावा या .NET), तो आपको निश्चित रूप से यह मान लेना चाहिए कि अपवादों को फेंकने के अलावा और कोई रास्ता नहीं है क्योंकि इन प्लेटफार्मों में अपवादों को फेंकने के लिए दिशानिर्देश हैं, और आपके ग्राहक उम्मीद करने वाले हैं इसलिए। अगर मैं आपकी लाइब्रेरी का उपयोग कर रहा था, तो मैं रिटर्न वैल्यूज़ की जाँच करने की जहमत नहीं उठाऊँगा क्योंकि मुझे उम्मीद है कि अपवादों को फेंक दिया जाएगा, यही है कि इन प्लेटफार्मों में दुनिया कैसी है।

हालाँकि, अगर यह C ++ था, तो यह निर्धारित करना थोड़ा अधिक चुनौतीपूर्ण होगा क्योंकि एक बड़ा कोडबेस पहले से ही रिटर्न कोड के साथ मौजूद है, और बड़ी संख्या में डेवलपर्स अपवादों के विरोध में मान लौटाने के लिए तैयार हैं (जैसे Windows HRES के साथ व्याप्त है) । इसके अलावा, कई अनुप्रयोगों में, यह एक प्रदर्शन मुद्दा भी हो सकता है (या कम से कम माना जाता है)।


5
Windows अपनी सार्वजनिक API में C संगतता बनाए रखने के लिए C ++ फ़ंक्शंस से HRESULT मान लौटाता है (और क्योंकि आप सीमाओं के पार मार्शल अपवादों की कोशिश कर चोट की दुनिया में उतरना शुरू करते हैं)। यदि आप एप्लिकेशन लिख रहे हैं, तो ऑपरेटिंग सिस्टम के मॉडल का आँख बंद करके पालन न करें।
कोडी ग्रे

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

2
@TrentonMaki यदि आप C ++ कंस्ट्रक्टर्स से त्रुटियों के बारे में बात कर रहे हैं, तो सबसे अच्छा जवाब यहाँ है: parashift.com/c++-faq-lite/ctors-can-throw.html । संक्षेप में, एक अपवाद को फेंक दें, लेकिन पहले संभावित लीक को साफ करना याद रखें। मैं किसी भी अन्य भाषाओं के बारे में नहीं जानता, जहाँ एक रचनाकार से सीधे फेंकना एक बुरी बात है। मुझे लगता है कि एपीआई के अधिकांश उपयोगकर्ता चेक त्रुटि कोड के बजाय अपवादों को पकड़ना पसंद करेंगे।
ओग्रे भजन 33

3
"ऐसे उन्नत परिदृश्य रिटर्न मान के साथ लागू करना आसान नहीं है।" जरूर आप कर सकते हो! आपको बस एक ErrorStateReturnVariableसुपर-क्लास बनाना है, और इसका एक गुण है InnerErrorState(जो कि एक उदाहरण है ErrorStateReturnVariable), जो उप-कक्षाओं को लागू करने से त्रुटियों की एक श्रृंखला दिखाने के लिए सेट हो सकता है ... ओह, रुको। : पी
ब्रायन एस

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

21

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

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


1
स्पष्टीकरण के लिए धन्यवाद! आपके उत्तर + ओमेर इकबाल ने मेरे प्रश्न का उत्तर दिया।
मिकायला माकी

"चेतावनियों" को संभालने का एक अन्य तरीका यह है कि अपवाद को डिफ़ॉल्ट रूप से फेंक दिया जाए और अपवाद को फेंकने से रोकने के लिए किसी प्रकार का वैकल्पिक ध्वज हो।
सियानफिश

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

एक अपवाद केवल तभी फेंका जाना चाहिए जब एक विफलता, अज्ञात या अपरिवर्तनीय परिदृश्य का अनुभव किया गया हो - असाधारण परिस्थितियों । अपवादों का निर्माण करते समय आपको प्रदर्शन के प्रभाव की उम्मीद करनी चाहिए
Gusdor

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

20

त्रुटि का संकेत देने के कई तरीके हैं:

त्रुटि चर की समस्या यह है कि जांचना भूल जाना आसान है।

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

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

पॉलीमॉर्फिक रिटर्न ( Either a bहास्केल में) मेरा अब तक का पसंदीदा समाधान है:

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

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


1
अच्छा उत्तर! मैं उम्मीद कर रहा था कि मुझे हैंडलर त्रुटियों की एक सूची मिल सकती है।
मिकायला माकी

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

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

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

12

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

आप इस तरह से कोड को समाप्त करते हैं:

String result = x.doActionA();
if (result != null) {
  throw new Exception(result);
}
result = x.doActionB();
if (result != null) {
  throw new Exception(result);
}

या यह:

if (!x.doActionA()) {
  throw new Exception(x.getError());
}
if (!x.doActionB()) {
  throw new Exception(x.getError());
}

मैं बहुत अधिक कार्रवाई करना चाहता हूं, लेकिन आप खुद अपवाद छोड़ देते हैं, इसलिए आप कुछ इस तरह से समाप्त करते हैं:

x.doActionA();
x.doActionB();

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

वर्तमान (भयानक) कोड:

private String doActionA() {
  try {
    someOperationThatCanGoWrong1();
    someOperationThatCanGoWrong2();
    someOperationThatCanGoWrong3();
    return null;
  } catch(Exception e) {
    return "Something went wrong!";
  }
}

नया और बेहतर:

private void doActionA() throws Exception {
  someOperationThatCanGoWrong1();
  someOperationThatCanGoWrong2();
  someOperationThatCanGoWrong3();
}

स्टैक ट्रेस संरक्षित है और संदेश अपवाद में उपलब्ध है, बेकार के बजाय "कुछ गलत हो गया!"।

आप निश्चित रूप से, बेहतर त्रुटि संदेशों की आपूर्ति कर सकते हैं, और आपको करना चाहिए। लेकिन यह पोस्ट यहाँ है क्योंकि मैं जिस वर्तमान कोड पर काम कर रहा हूं वह एक दर्द है, और आपको ऐसा नहीं करना चाहिए।


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

1
यह सच है, लेकिन वर्तमान में यहाँ नहीं होता है। और आप हमेशा doActionA में अपवाद को पकड़ सकते हैं और एक स्थिति संदेश के साथ दूसरे अपवाद में लपेट सकते हैं। तब आपके पास स्टैक ट्रेस और उपयोगी संदेश होगा। throw new Exception("Something went wrong with " + instanceVar, ex);
मर्जिंक

मैं सहमत हूं, यह आपकी स्थिति में नहीं हो सकता है। लेकिन आप जानकारी को हमेशा doActionA () में नहीं रख सकते। क्यों? DoActionA () का कॉलर केवल वही जानकारी हो सकती है जिसे आपको शामिल करने की आवश्यकता है।
सूचित

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

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

5

"हो रही कई संभावित त्रुटियों को संभालने के लिए, निष्पादन को रोकना नहीं चाहिए,"

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

दो पैटर्न जो मैंने उपयोग किए हैं वे हैं:

  • एक त्रुटि / चेतावनी संग्रह, या तो पारित या सदस्य चर के रूप में रखा गया। जिसे आप सामान में जोड़ते हैं और सिर्फ प्रोसेसिंग करते रहते हैं। मैं व्यक्तिगत रूप से इस दृष्टिकोण को वास्तव में पसंद नहीं करता हूं क्योंकि मुझे लगता है कि यह कॉल करने वाले को अक्षम करता है।

  • त्रुटि / चेतावनी हैंडलर ऑब्जेक्ट में पास करें (या इसे सदस्य चर के रूप में सेट करें)। और प्रत्येक त्रुटि हैंडलर के सदस्य फ़ंक्शन को कॉल करती है। इस तरह से कॉलर यह तय कर सकता है कि ऐसी गैर-समाप्ति त्रुटियों के साथ क्या करना है।

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

एक त्रुटि हैंडलर का उपयोग करते हुए विशिष्ट कोड इस तरह दिख सकता है

class MyFunClass {
  public interface ErrorHandler {
     void onError(Exception e);
     void onWarning(Exception e);
  }

  ErrorHandler eh;

  public void canFail(int i) {
     if(i==0) {
        if(eh!=null) eh.onWarning(new Exception("canFail shouldn't be called with i=0"));
     }
     if(i==1) {
        if(eh!=null) eh.onError(new Exception("canFail called with i=1 is fatal");
        throw new RuntimeException("canFail called with i=2");
     }
     if(i==2) {
        if(eh!=null) eh.onError(new Exception("canFail called with i=2 is an error, but not fatal"));
     }
  }
}

3
+1 यह ध्यान देने के लिए कि उपयोगकर्ता एक चेतावनी चाहता है, बजाय एक त्रुटि के। अजगर के warningsपैकेज का उल्लेख करने योग्य हो सकता है , जो इस समस्या के लिए एक और पैटर्न देता है।
जेम्स_पिक

शानदार जवाब के लिए धन्यवाद! यह वही है जो मैं देखना चाहता था, त्रुटियों को संभालने के लिए अतिरिक्त पैटर्न जहां पारंपरिक कोशिश / पकड़ पर्याप्त नहीं हो सकती है।
मिकायला माकी

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

5

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


4

इस सवाल का जवाब पहले से ही है, लेकिन मैं खुद की मदद नहीं कर सकता।

आप वास्तव में सभी उपयोग मामलों के लिए समाधान प्रदान करने के लिए अपवाद की उम्मीद नहीं कर सकते। किसी को हैमर?

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

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

तो यहाँ सबक है: अपवाद के पास अपने स्थान हैं, जैसा कि त्रुटि कोड हैं। बुद्धिमानी से चुना।


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

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

और आप एक मूर्खतापूर्ण मजबूत-लेकिन-नहीं-वास्तव में कमजोर पासवर्ड के साथ क्या करेंगे? आप प्रवाह को बाधित करेंगे और उपयोगकर्ता को यह दर्शाते हुए संदेश दिखाएंगे कि दर्ज किया गया पासवर्ड बहुत मजबूत नहीं है। तो एक MiddlyStrongValidatorया कुछ है। और अगर वह वास्तव में आपके प्रवाह को बाधित नहीं करता है, Validatorतो उसे पहले से ही बुलाया जाना चाहिए, जबकि प्रवाह को आगे बढ़ाने से पहले, जबकि उपयोगकर्ता अभी भी अपने पासवर्ड (या समान) में प्रवेश कर रहा है। लेकिन तब सत्यापन पहली जगह में सवाल में विधि का हिस्सा नहीं था। :) शायद सब के बाद स्वाद की बात है ...
jr

@jhr मेरे द्वारा लिखे गए सत्यापनकर्ताओं में, मैं आमतौर पर एक AggregateException(या एक समान ValidationException) बनाऊंगा, और इनर एक्सिडेंस में प्रत्येक सत्यापन मुद्दे के लिए विशिष्ट अपवाद डालूंगा। उदाहरण के लिए, यह हो सकता है BadPasswordException: "उपयोगकर्ता का पासवर्ड न्यूनतम लंबाई 6 से कम है" या MandatoryFieldMissingException: "उपयोगकर्ता के लिए पहला नाम प्रदान किया जाना चाहिए" आदि यह त्रुटि कोड के बराबर नहीं है। इन सभी संदेशों को उपयोगकर्ता को इस तरह से प्रदर्शित किया जा सकता है कि वे समझ जाएंगे, और अगर NullReferenceExceptionइसके बजाय फेंक दिया गया था, तो हमें एक बग मिला।
ओमर इकबाल

4

उपयोग के मामले हैं त्रुटि कोड अपवादों के लिए बेहतर थे।

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

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


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

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

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

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

1
अपवादों का उपयोग करने पर एक गारंटी है: यदि आप उन्हें नहीं पकड़ते हैं, तो वे यूआई तक सबसे अधिक मामले - सबसे खराब स्थिति में फेंक देते हैं। ऐसी कोई गारंटी नहीं यदि आप रिटर्न कोड का उपयोग करते हैं जो कोई नहीं पढ़ता है। यदि आप इसका उपयोग करना चाहते हैं, तो निश्चित रूप से, एपीआई का अनुसरण करें। मैं सहमत हूँ! लेकिन वहाँ त्रुटि के लिए जगह है, दुर्भाग्य से ...
18'14

2

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

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


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

1

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

में पर्ल 6 यह किया के माध्यम से किया जाता है fail, जो अगर भीतर no fatal;गुंजाइश एक विशेष unthrown अपवाद रिटर्न Failureवस्तु।

में पर्ल 5 आप उपयोग कर सकते हैं प्रासंगिक :: वापसी आप के साथ ऐसा कर सकते हैं return FAIL


-1

जब तक कुछ बहुत विशिष्ट नहीं है, मुझे लगता है कि सत्यापन के लिए त्रुटि चर एक बुरा विचार है। उद्देश्य सत्यापन पर खर्च किए गए समय की बचत के बारे में प्रतीत होता है (आप केवल परिवर्तनीय मूल्य वापस कर सकते हैं)

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

संपादित करें: मुझे नहीं पता था कि यह एक विशिष्ट मामले के बजाय सॉफ्टवेयर प्रतिमान का सवाल है।

मुझे मेरे एक विशेष मामले में अपनी बातों को और स्पष्ट करना चाहिए जिसमें मेरा उत्तर समझ में आएगा

  1. मेरे पास इकाई वस्तुओं का एक संग्रह है
  2. मेरे पास प्रक्रियात्मक शैली की वेब सेवाएँ हैं जो इन इकाई वस्तुओं के साथ काम करती हैं

त्रुटियों के दो प्रकार हैं:

  1. सर्विस लेयर में प्रोसेसिंग करते समय होने वाली त्रुटियां
  2. त्रुटियों क्योंकि इकाई वस्तुओं में विसंगतियां हैं

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

मैं दूसरी तरह की त्रुटि के बारे में सोच रहा था। और मेरा जवाब इस दूसरी तरह की त्रुटियों के बारे में है। इकाई वस्तुओं में, हमारे लिए विकल्प हैं, उनमें से कुछ हैं

  • एक सत्यापन चर का उपयोग करना
  • जब कोई फ़ील्ड सेट्टर से गलत तरीके से सेट की जाती है तो तुरंत एक अपवाद को फेंक दें

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

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


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