अपवाद - "क्या हुआ" बनाम "क्या करना है"


19

हम कोड के उपभोक्ता को एक उपयोगी तरीके से अनपेक्षित व्यवहार से निपटने के लिए अपवादों का उपयोग करते हैं। आमतौर पर अपवाद "क्या हुआ" परिदृश्य के आसपास बनाए जाते हैं - जैसे FileNotFound(हम आपके द्वारा निर्दिष्ट फ़ाइल खोजने में असमर्थ थे) या ZeroDivisionError(हम 1/0ऑपरेशन करने में असमर्थ थे )।

क्या होगा यदि उपभोक्ता के अपेक्षित व्यवहार को निर्दिष्ट करने की संभावना है?

उदाहरण के लिए, कल्पना करें कि हमारे पास fetchसंसाधन हैं, जो HTTP अनुरोध करता है और पुनर्प्राप्त डेटा देता है। और त्रुटियों के बजाय जैसे ServiceTemporaryUnavailableया RateLimitExceededहम RetryableErrorउपभोक्ता को केवल एक सुझाव देते हैं कि उसे केवल अनुरोध को फिर से लेना चाहिए और विशिष्ट विफलता की परवाह नहीं करनी चाहिए। तो, हम मूल रूप से फोन करने वाले को एक कार्रवाई का सुझाव दे रहे हैं - "क्या करना है"।

हम ऐसा अक्सर नहीं करते हैं क्योंकि हम उपभोक्ताओं के सभी उपयोगिताओं को नहीं जानते हैं। लेकिन कल्पना कीजिए कि कुछ विशिष्ट घटक हैं जो हम एक कॉलर के लिए कार्यों का सबसे अच्छा कोर्स जानते हैं - तो क्या हमें "क्या करना है" दृष्टिकोण का उपयोग करना चाहिए?


3
क्या HTTP पहले से ऐसा नहीं करता है? 503 जवाब देने के लिए एक अस्थायी विफलता है, इसलिए आवश्यककर्ता को फिर से प्रयास करना चाहिए, 404 एक मौलिक अनुपस्थिति है, इसलिए इसका पुनः प्रयास करने का कोई मतलब नहीं है, 301 का अर्थ है "स्थायी रूप से स्थानांतरित", इसलिए आपको फिर से प्रयास करना चाहिए, लेकिन एक अलग पते के साथ, आदि
सिल्विया फोथ

7
कई मामलों में, यदि हम वास्तव में "क्या करना है" जानते हैं, तो हम कंप्यूटर को स्वचालित रूप से कर सकते हैं और उपयोगकर्ता को यह भी पता नहीं है कि कुछ भी गलत हो गया है। मुझे लगता है कि जब भी मेरा ब्राउज़र 301 प्राप्त करता है, तो वह मुझसे पूछे बिना ही नए पते पर चला जाता है।
Ixrec

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

1
@ रोमनबोडनार्चुक: मैं असहमत हूं। यह कहने की तरह है कि एक व्यक्ति को चीनी बोलने के लिए चीनी को जानने की आवश्यकता नहीं होनी चाहिए। HTTP एक प्रोटोकॉल है, और क्लाइंट और सर्वर दोनों को इसे जानने और इसका पालन करने की उम्मीद है। कि एक प्रोटोकॉल कैसे काम करता है। यदि केवल एक पक्ष जानता है और उसका पालन करता है, तो आप संवाद नहीं कर सकते।
क्रिस प्रैट

1
यह ईमानदारी से लगता है कि आप अपने अपवादों को कैच ब्लॉक से बदलने की कोशिश कर रहे हैं। यही कारण है कि हम अपवादों में चले गए - कोई और नहीं if( ! function() ) handle_something();, लेकिन त्रुटि को संभालने में सक्षम होने के नाते आप वास्तव में कॉलिंग संदर्भ जानते हैं - यानी एक क्लाइंट को एक एसवाईएस व्यवस्थापक को कॉल करने के लिए कहें, यदि आपका सर्वर विफल हो गया या यदि कनेक्शन गिरा दिया गया तो स्वचालित रूप से पुनः लोड करें, लेकिन आपको अलर्ट करें मामला यह है कि कॉल करने वाला एक और माइक्रोसेवक है। पकड़ने वाले ब्लॉक को पकड़ने दें।
Sebb

जवाबों:


47

लेकिन कल्पना करें कि कुछ विशिष्ट घटक हैं जो हम एक कॉलर के लिए क्रियाओं का सबसे अच्छा कोर्स जानते हैं।

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

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

उन्हें चुनाव दो


4
मैं सहमत हूँ। सर्वर को केवल जानकारी को ठीक से बताना चाहिए। दूसरी ओर प्रलेखन को स्पष्ट रूप से ऐसे मामलों में कार्रवाई के उचित पाठ्यक्रम की रूपरेखा तैयार करनी चाहिए।
नील

1
इसका एक छोटा दोष यह है कि कुछ हैकर्स के लिए, कुछ अपवाद उन्हें काफी कुछ बता सकते हैं कि आपका कोड क्या कर रहा है और वे इसका उपयोग करने के तरीकों का पता लगाने के लिए इसका उपयोग कर सकते हैं।
फराप

3
@Pharap यदि आपके हैकर्स के पास त्रुटि संदेश के बजाय स्वयं अपवाद है, तो आप पहले ही खो चुके हैं।
corsiKa

2
मुझे यह उत्तर पसंद है, लेकिन यह याद आ रहा है कि मैं अपवादों की क्या आवश्यकता समझता हूं ... यदि आप जानते थे कि कैसे पुनर्प्राप्त करना है, तो यह अपवाद नहीं होगा! एक अपवाद केवल तब होना चाहिए जब आप इसके बारे में कुछ नहीं कर सकते हैं: अमान्य इनपुट, अमान्य स्थिति, अमान्य सुरक्षा - आप उन प्रोग्राम को ठीक नहीं कर सकते।
corsiKa

1
माना। यदि आप यह संकेत देने के लिए जोर देते हैं कि क्या रिट्रीट संभव है, तो आप हमेशा संबंधित ठोस अपवादों को विरासत में प्राप्त कर सकते हैं RetryableError
सपि

18

चेतावनी! सी + + प्रोग्रामर संभवत: अलग-अलग विचारों के साथ यहां आ रहा है कि कैसे अपवाद-हैंडलिंग को एक सवाल का जवाब देने की कोशिश की जानी चाहिए जो निश्चित रूप से किसी अन्य भाषा के बारे में है!

इस विचार को देखते हुए:

उदाहरण के लिए, कल्पना करें कि हमारे पास संसाधन हैं, जो HTTP अनुरोध करता है और पुनर्प्राप्त डेटा लौटाता है। और ServiceTemporaryUnavailable या RateLimitExceeded जैसी त्रुटियों के बजाय, हम सिर्फ उपभोक्ता को सुझाव देते हुए एक रिट्रीएबल जुटाएंगे कि यह केवल अनुरोध को पुनः प्रयास करें और विशिष्ट विफलता के बारे में परवाह न करें।

... एक बात जो मैं सुझाऊँगा, वह यह हो सकता है कि आप कार्रवाई के पाठ्यक्रमों के साथ एक त्रुटि की रिपोर्ट करने की चिंताओं को इस तरह से जवाब दे सकते हैं जो आपके कोड की व्यापकता को कम कर सकता है या अपवादों के लिए "अनुवाद बिंदुओं" की बहुत आवश्यकता हो सकती है। ।

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

कोई फर्क नहीं पड़ता कि क्या होता है, मान लें कि कार्रवाई का कोर्स उपयोगकर्ता को क्या हुआ है और उसे इसके बारे में क्या करना है, उसके बारे में उसे सूचित करना है ("पुनः प्रयास करें, किसी अन्य फ़ाइल को लोड करें, रद्द करें")।

फेंकने वाला बनाम पकड़ने वाला

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

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

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

द ब्लाइंड थ्रोअर

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

उल्टे जिम्मेदारियों और पकड़ने वाले को सामान्य बनाना

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

Lightness Races in Orbit'sठीक जवाब से बुद्धिमान सलाह को ध्यान में रखते हुए (जो मुझे लगता है कि वास्तव में एक उन्नत पुस्तकालय उन्मुख मानसिकता से आ रहा है), आपको अभी भी "क्या करें" अपवादों को फेंकने के लिए लुभाया जा सकता है, केवल लेनदेन वसूली साइट के करीब।

यहां से एक मध्यस्थ, सामान्य लेनदेन से निपटने वाली साइट को ढूंढना संभव हो सकता है जो वास्तव में "क्या करना है" चिंताओं को केंद्रीकृत करता है लेकिन अभी भी पकड़ने के संदर्भ में है।

यहाँ छवि विवरण दर्ज करें

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

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

def general_catcher(task):
    try:
       task()
    except SomeError1:
       # do some uniformly-designed recovery stuff here
    except SomeError2:
       # do some other uniformly-designed recovery stuff here
    ...

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

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


2
+1। मैंने पहले कभी भी "ब्लाइंड थ्रोअर" विचार के बारे में नहीं सुना था, लेकिन यह इस बात में फिट बैठता है कि मैं अपवाद से निपटने के बारे में कैसे सोचता हूं: त्रुटि उत्पन्न हुई, यह मत सोचिए कि इसे कैसे संभाला जाना चाहिए। जब आप पूर्ण स्टैक के लिए ज़िम्मेदार होते हैं तो ज़िम्मेदारियों को साफ़-साफ़ अलग करना कठिन (लेकिन महत्वपूर्ण है!) और समस्याओं के बारे में सूचित करने के लिए कैली ज़िम्मेदार होता है, और समस्याओं से निपटने के लिए फोन करने वाला। कैली केवल वही जानती है जो उसे करने के लिए कहा गया था, क्यों नहीं। हैंडलिंग त्रुटियों को 'क्यों' के संदर्भ में किया जाना चाहिए, इसलिए: कॉलर में।
सोजेरड जॉब पोस्टम्यूस

1
(इसके अलावा, मुझे नहीं लगता कि आपका उत्तर C ++ विशिष्ट है, लेकिन सामान्य रूप से अपवाद से निपटने के लिए लागू है)
Sjoerd Job Postmus

1
@SjoerdJobPostmus Yay धन्यवाद! "ब्लाइंड थ्रोअर" सिर्फ एक नासमझ सादृश्य है जिसे मैं यहाँ लेकर आया था - मैं जटिल तकनीकी अवधारणाओं को पचाने में बहुत स्मार्ट या त्वरित नहीं हूँ, इसलिए मैं अक्सर अपनी समझ को बेहतर बनाने और बेहतर बनाने के लिए छोटी कल्पना और उपमाएँ खोजना चाहता हूँ। की चीज़ों का। हो सकता है कि किसी दिन मैं कार्टून की बहुत सारी तस्वीरों से भरी एक छोटी सी प्रोग्रामिंग बुक लिखने की कोशिश करूं। :-D

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

1
@DrunkCoder: कृपया अपने पदों के साथ बर्बरता न करें। हमारे पास इंटरनेट पर पहले से ही पर्याप्त बोरेन सामान है। यदि आपके पास हटाने का एक अच्छा कारण है, तो मध्यस्थ ध्यान के लिए अपनी पोस्ट को चिह्नित करें और अपना मामला बनाएं।
रॉबर्ट हार्वे

2

मुझे लगता है कि अधिकांश समय यह बेहतर होगा कि फ़ंक्शन को तर्क देकर यह बताया जाए कि उन परिस्थितियों को कैसे संभालना है।

उदाहरण के लिए, एक फ़ंक्शन पर विचार करें:

Response fetchUrl(URL url, RetryPolicy retryPolicy);

मैं RetryPolicy.noRetries () या RetryPolicy.retries (3) या जो कुछ भी पास कर सकता हूं। मामले में पुन: प्रयोज्य विफलता के मामले में, यह नीति को यह तय करने के लिए परामर्श करेगा कि क्या इसे वापस लेना चाहिए या नहीं।


हालाँकि, कॉल साइट पर अपवादों को फेंकने का वास्तव में बहुत कुछ नहीं है, हालाँकि। आप कुछ अलग बात कर रहे हैं, जो कि ठीक है लेकिन वास्तव में सवाल का हिस्सा नहीं है ..
मोनिका के साथ लाइटनेस दौड़

@LightnessRacesinOrbit, इसके विपरीत। मैं इसे अपवाद के रूप में कॉल साइट पर वापस फेंकने के विचार के रूप में प्रस्तुत कर रहा हूं। ओपी के उदाहरण में, fetchUrl एक RetryableException को फेंक देगा, और मैं कह रहा हूं कि आपको fetchUrl को यह बताना चाहिए कि इसे कब पुन: प्रयास करना चाहिए।
विंस्टन एवर्ट

2
@WinstonEwert: हालांकि मैं लाइटनेसरेंसिनऑर्बिट से सहमत हूं, मैं आपकी बात भी देखता हूं, लेकिन इसे उसी नियंत्रण का प्रतिनिधित्व करने का एक अलग तरीका समझें। लेकिन, इस पर विचार करें कि आप शायद पास new RetryPolicy().onRateLimitExceeded(STOP).onServiceTemporaryUnavailable(RETRY, 3)या कुछ करना चाहते हैं , क्योंकि इससे RateLimitExceededअलग तरीके से निपटने की आवश्यकता हो सकती है ServiceTemporaryUnavailable। यह लिखने के बाद, मेरा विचार है: बेहतर एक अपवाद को फेंक दें, क्योंकि यह अधिक लचीला नियंत्रण देता है।
Sjoerd Job Postmus

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