मुझे लगता है कि मैंने वही ब्रूस एकेल इंटरव्यू पढ़ा था जो आपने किया था - और यह हमेशा मुझे बुरा लगा। वास्तव में, साक्षात्कारकर्ता द्वारा तर्क दिया गया था (यदि यह वास्तव में वह पद है जिसके बारे में आप बात कर रहे हैं) एंडर्स हेजलबर्ग, .NET और C # के पीछे एमएस जीनियस हैं।
http://www.artima.com/intv/handcuffs.html
फैन हालांकि मैं हेजलबर्ग और उनके काम का हूं, लेकिन इस तर्क ने मुझे हमेशा संगीन बना दिया है। यह मूल रूप से उबलता है:
"चेक किए गए अपवाद बुरे हैं क्योंकि प्रोग्रामर उन्हें हमेशा उन्हें पकड़ने और उन्हें खारिज करने का दुरुपयोग करते हैं जिससे समस्याओं को छिपाया जाता है और अनदेखा किया जाता है जो अन्यथा उपयोगकर्ता को प्रस्तुत किया जाएगा"।
द्वारा "अन्यथा उपयोगकर्ता के लिए प्रस्तुत" यदि आप एक क्रम अपवाद का उपयोग मेरा मतलब है आलसी प्रोग्रामर बस पर ध्यान नहीं देगा यह (एक खाली कैच ब्लॉक के साथ पकड़ने की तुलना में) और उपयोगकर्ता इसे देखेंगे।
तर्क के सारांश का सारांश यह है कि "प्रोग्रामर उन्हें ठीक से उपयोग नहीं करेंगे और उनका ठीक से उपयोग नहीं करना उनके नहीं होने से भी बदतर है" ।
इस तर्क में कुछ सच्चाई है और वास्तव में, मुझे शक है कि जावा में ऑपरेटर ओवरराइड्स नहीं डालने के लिए गोस्लिंग प्रेरणा एक समान तर्क से आती है - वे प्रोग्रामर को भ्रमित करते हैं क्योंकि वे अक्सर दुर्व्यवहार करते हैं।
लेकिन अंत में, मैं इसे हेजेल्सबर्ग का फर्जी तर्क मानता हूं और संभवतः एक पोस्ट-हॉक जो एक अच्छी तरह से सोचा गया निर्णय के बजाय कमी को समझाने के लिए बनाया गया है।
मेरा तर्क है कि चेक किए गए अपवादों का अधिक उपयोग एक बुरी बात है और उपयोगकर्ताओं द्वारा मैला काम करने की ओर ले जाता है, लेकिन उनका उचित उपयोग एपीआई प्रोग्रामर को एपीआई क्लाइंट प्रोग्रामर को बहुत लाभ देता है।
अब एपीआई प्रोग्रामर को सावधान रहना होगा कि सभी जगहों पर चेक किए गए अपवादों को न फेंके, या वे केवल क्लाइंट प्रोग्रामर को परेशान करेंगे। बहुत ही आलसी ग्राहक प्रोग्रामर को पकड़ने के लिए सहारा देगा (Exception) {}
क्योंकि हेजेल्सबर्ग चेतावनी देता है और सभी लाभ खो जाएंगे और नरक सुनिश्चित हो जाएगा। लेकिन कुछ परिस्थितियों में, एक अच्छे चेक किए गए अपवाद के लिए कोई विकल्प नहीं है।
मेरे लिए, क्लासिक उदाहरण फ़ाइल-ओपन एपीआई है। भाषाओं के इतिहास में हर प्रोग्रामिंग भाषा (फ़ाइल सिस्टम पर कम से कम) में एक एपीआई कहीं न कहीं है जो आपको एक फ़ाइल खोलने देता है। और इस API का उपयोग करने वाला प्रत्येक क्लाइंट प्रोग्रामर जानता है कि उन्हें उस मामले से निपटना होगा, जिस फ़ाइल को वे खोलने की कोशिश कर रहे हैं, वह मौजूद नहीं है। मुझे यह बताने की ज़रूरत है कि: इस API का उपयोग करने वाले प्रत्येक ग्राहक को पता होना चाहिए कि उन्हें इस मामले से निपटना है। और वहाँ रगड़ है: क्या एपीआई प्रोग्रामर उन्हें यह बताने में मदद कर सकता है कि उन्हें अकेले टिप्पणी करने के माध्यम से निपटना चाहिए या वे वास्तव में इसके लिए ग्राहक सौदे पर जोर दे सकते हैं ।
C में मुहावरा कुछ इस तरह से चलता है
if (f = fopen("goodluckfindingthisfile")) { ... }
else { // file not found ...
जहाँ fopen
0 और C को वापस करने में विफलता इंगित करती है (मूर्खतापूर्ण) आप 0 को एक बूलियन के रूप में मानते हैं और ... मूल रूप से, आप इस मुहावरे को सीखते हैं और आप ठीक हैं। लेकिन क्या हो अगर आप एक नॉब हैं और आपने मुहावरा नहीं सीखा है। फिर, बेशक, आप के साथ शुरू करते हैं
f = fopen("goodluckfindingthisfile");
f.read(); // BANG!
और कठिन तरीका सीखें।
ध्यान दें कि हम यहां केवल दृढ़ता से टाइप की गई भाषाओं के बारे में बात कर रहे हैं: एक स्पष्ट रूप से टाइप की गई भाषा में एक एपीआई क्या है, इसका स्पष्ट विचार है: यह प्रत्येक के लिए एक स्पष्ट रूप से परिभाषित प्रोटोकॉल के साथ उपयोग करने के लिए आपके लिए कार्यक्षमता (विधियों) का एक smorgasbord है।
यह स्पष्ट रूप से परिभाषित प्रोटोकॉल आमतौर पर एक विधि हस्ताक्षर द्वारा परिभाषित किया गया है। यहां फ़ोपेन की आवश्यकता है कि आप इसे एक स्ट्रिंग (या सी के मामले में एक चार *) पास करें। यदि आप इसे कुछ और देते हैं तो आपको एक संकलन-समय त्रुटि मिलती है। आपने प्रोटोकॉल का पालन नहीं किया - आप ठीक से एपीआई का उपयोग नहीं कर रहे हैं।
कुछ (अस्पष्ट) भाषाओं में रिटर्न प्रकार प्रोटोकॉल का भी हिस्सा है। यदि आप fopen()
इसे चर के लिए असाइन किए बिना कुछ भाषाओं के समतुल्य कॉल करने का प्रयास करते हैं, तो आपको एक संकलन-समय त्रुटि भी मिलेगी (आप केवल शून्य कार्यों के साथ ऐसा कर सकते हैं)।
जिस बिंदु को मैं बनाने की कोशिश कर रहा हूं, वह यह है: एक वैधानिक रूप से टाइप की गई भाषा में एपीआई प्रोग्रामर क्लाइंट को अपने क्लाइंट कोड को कंपाइल करने से रोकता है कि अगर वह कोई स्पष्ट गलती करता है, तो उसे ठीक से उपयोग करने के लिए एपीआई का उपयोग करने के लिए प्रोत्साहित करता है।
(डायनेमिक रूप से टाइप की गई भाषा में, रूबी की तरह, आप कुछ भी पास कर सकते हैं, एक फ्लोट कह सकते हैं, जैसा कि फ़ाइल नाम है - और यह संकलन करेगा। यदि आप विधि के तर्कों को नियंत्रित करने के लिए भी नहीं जा रहे हैं तो उपयोगकर्ता को जाँच किए गए अपवादों से परेशान क्यों करें।) यहां किए गए तर्क केवल वैधानिक रूप से टाइप की गई भाषाओं पर लागू होते हैं।)
तो, जाँच किए गए अपवादों के बारे में क्या?
अच्छी तरह से यहाँ जावा एपीआई आप एक फ़ाइल खोलने के लिए उपयोग कर सकते हैं।
try {
f = new FileInputStream("goodluckfindingthisfile");
}
catch (FileNotFoundException e) {
// deal with it. No really, deal with it!
... // this is me dealing with it
}
देखिए वो कैच? यहाँ उस एपीआई विधि के लिए हस्ताक्षर हैं:
public FileInputStream(String name)
throws FileNotFoundException
ध्यान दें कि FileNotFoundException
एक जाँच अपवाद है।
एपीआई प्रोग्रामर आपसे यह कह रहा है: "आप इस निर्माता का उपयोग एक नया FileInputStream बनाने के लिए कर सकते हैं लेकिन आप
a) स्ट्रिंग नाम के रूप में फ़ाइल नाम में पास होना चाहिए
) इस संभावना को स्वीकार करना चाहिए कि फ़ाइल रनटाइम पर नहीं मिल सकती है "
और यह पूरा बिंदु है जहाँ तक मेरा सवाल है।
कुंजी मूल रूप से क्या है जो प्रश्न "चीजें जो प्रोग्रामर के नियंत्रण से बाहर हैं" के रूप में बताती हैं। मेरा पहला विचार यह था कि वह / वह चीजें हैं जो एपीआई प्रोग्रामर नियंत्रण से बाहर हैं । लेकिन वास्तव में, जब अपवाद का उपयोग किया जाता है तो वास्तव में उन चीजों के लिए होना चाहिए जो क्लाइंट प्रोग्रामर और एपीआई प्रोग्रामर के नियंत्रण से बाहर हैं। मुझे लगता है कि चेक किए गए अपवादों का दुरुपयोग न करने की यही कुंजी है।
मुझे लगता है कि फ़ाइल-ओपन बिंदु को अच्छी तरह से दिखाता है। एपीआई प्रोग्रामर जानता है कि आप उन्हें एक फ़ाइल नाम दे सकते हैं, जो एपीआई कहे जाने के समय किसी के भी पास नहीं है, और यह कि वे आपको वह नहीं लौटा पाएंगे जो आप चाहते थे, लेकिन आपको एक अपवाद फेंकना होगा। वे यह भी जानते हैं कि यह बहुत नियमित रूप से होगा और क्लाइंट प्रोग्रामर फ़ाइल नाम को उस समय सही होने की उम्मीद कर सकता है जब उन्होंने कॉल लिखा था, लेकिन यह उनके नियंत्रण से परे कारणों के लिए रनटाइम पर गलत हो सकता है।
तो एपीआई इसे स्पष्ट करता है: ऐसे मामले होंगे जहां यह फ़ाइल उस समय मौजूद नहीं होती है जब आप मुझे कॉल करते हैं और आपने इसके साथ बेहतर व्यवहार किया है।
यह एक काउंटर-केस के साथ स्पष्ट होगा। कल्पना कीजिए कि मैं एक टेबल एपीआई लिख रहा हूं। मेरे पास इस पद्धति सहित एपीआई के साथ टेबल मॉडल कहीं है:
public RowData getRowData(int row)
अब मैं एक एपीआई प्रोग्रामर के रूप में जानता हूं कि ऐसे मामले होंगे जहां कुछ क्लाइंट पंक्ति के लिए एक नकारात्मक मूल्य या तालिका के बाहर एक पंक्ति मूल्य से गुजरता है। इसलिए मुझे एक अपवाद को फेंकने के लिए लुभाया जा सकता है और ग्राहक को इससे निपटने के लिए मजबूर कर सकता है:
public RowData getRowData(int row) throws CheckedInvalidRowNumberException
(मैं वास्तव में इसे "जाँच" निश्चित रूप से नहीं कहूंगा।)
यह जाँच किए गए अपवादों का बुरा उपयोग है। ग्राहक कोड पंक्ति डेटा प्राप्त करने के लिए कॉल से भरा होने जा रहा है, जिसमें से प्रत्येक को एक कोशिश / कैच का उपयोग करना होगा, और किसके लिए? क्या वे उपयोगकर्ता को रिपोर्ट करने जा रहे हैं कि गलत पंक्ति मांगी गई थी? शायद नहीं - क्योंकि मेरे टेबल व्यू के आसपास जो भी यूआई है, वह उपयोगकर्ता को ऐसी स्थिति में नहीं जाने देना चाहिए जहां एक अवैध पंक्ति का अनुरोध किया जा रहा है। तो यह क्लाइंट प्रोग्रामर की ओर से एक बग है।
एपीआई प्रोग्रामर अभी भी भविष्यवाणी कर सकता है कि ग्राहक ऐसे बगों को कोड करेगा और इसे एक रनटाइम अपवाद जैसे ए के साथ संभालना चाहिए IllegalArgumentException
।
में एक अपवाद के अपवाद के साथ getRowData
, यह स्पष्ट रूप से एक ऐसा मामला है जो हेजलबर्ग के आलसी प्रोग्रामर को केवल खाली कैच जोड़ने के लिए नेतृत्व करने वाला है। जब ऐसा होता है, अवैध पंक्ति मान परीक्षक या क्लाइंट डेवलपर डिबगिंग के लिए भी स्पष्ट नहीं होगा, बल्कि वे उन त्रुटियों पर दस्तक देंगे, जिनके स्रोत को इंगित करना कठिन है। लॉन्च के बाद एरियन रॉकेट उड़ जाएंगे।
ठीक है, इसलिए यहां समस्या है: मैं कह रहा हूं कि चेक किए गए अपवाद FileNotFoundException
क्लाइंट प्रोग्रामर के लिए सबसे उपयोगी तरीके से एपीआई को परिभाषित करने के लिए एपीआई प्रोग्रामर टूलबॉक्स में एक आवश्यक उपकरण नहीं है। लेकिन CheckedInvalidRowNumberException
एक बड़ी असुविधा है, जो खराब प्रोग्रामिंग के लिए अग्रणी है और इसे टाला जाना चाहिए। लेकिन अंतर कैसे बताया जाए।
मुझे लगता है कि यह एक सटीक विज्ञान नहीं है और मुझे लगता है कि अंडरलाइज़ और शायद एक हद तक हेज्सबर्ग के तर्क को सही ठहराता है। लेकिन मैं खुश नहीं हूँ कि बच्चे को यहाँ नहाने के पानी के साथ फेंक दिया जाए, इसलिए मुझे यहाँ से कुछ अच्छे नियमों को अलग करने के लिए कुछ नियम निकालने की अनुमति दें:
ग्राहक के नियंत्रण से बाहर या बंद बनाम खुला:
चेक किए गए अपवादों का उपयोग केवल वहीं किया जाना चाहिए जहां त्रुटि का मामला एपीआई और क्लाइंट प्रोग्रामर दोनों के नियंत्रण से बाहर हो । यह सिस्टम को कितना खुला या बंद करना है, यह करना होगा। एक विवश यूआई में जहां क्लाइंट प्रोग्रामर का नियंत्रण होता है, कहते हैं, बटन, कीबोर्ड कमांड आदि सभी पर, जो टेबल व्यू (एक बंद सिस्टम) से पंक्तियों को जोड़ते हैं और हटाते हैं, यह एक क्लाइंट प्रोग्रामिंग बग है यदि यह डेटा लाने का प्रयास करता है कोई नहीं के बराबर पंक्ति। एक फ़ाइल-आधारित ऑपरेटिंग सिस्टम में, जहाँ कोई भी उपयोगकर्ता / एप्लिकेशन फ़ाइलों (एक खुली प्रणाली) को जोड़ और हटा सकते हैं, यह अनुमान योग्य है कि ग्राहक जिस फ़ाइल का अनुरोध कर रहा है, उसे उनके ज्ञान के बिना हटा दिया गया है, इसलिए उनसे निपटने की उम्मीद की जानी चाहिए। ।
सर्वव्यापकता:
क्लाइंट द्वारा अक्सर किए गए API कॉल पर चेक किए गए अपवादों का उपयोग नहीं किया जाना चाहिए। अक्सर मैं क्लाइंट कोड में बहुत से स्थानों से मतलब रखता हूं - अक्सर समय में नहीं। इसलिए एक क्लाइंट कोड एक ही फ़ाइल को बहुत खोलने की कोशिश नहीं करता है, लेकिन मेरा टेबल व्यू RowData
अलग-अलग तरीकों से सभी जगह मिलता है। विशेष रूप से, मैं बहुत सारे कोड लिखूंगा
if (model.getRowData().getCell(0).isEmpty())
और हर बार कोशिश / पकड़ में लपेटना दर्दनाक होगा।
उपयोगकर्ता को सूचित करना:
चेक किए गए अपवादों का उपयोग उन मामलों में किया जाना चाहिए जहां आप अंतिम उपयोगकर्ता के लिए प्रस्तुत किए जा रहे एक उपयोगी त्रुटि संदेश की कल्पना कर सकते हैं। यह "और क्या होगा जब आप ऐसा करेंगे?" सवाल मैंने ऊपर उठाया। यह आइटम 1 से भी संबंधित है। जब से आप यह अनुमान लगा सकते हैं कि आपके क्लाइंट-एपीआई सिस्टम के बाहर की कोई चीज फाइल नहीं हो सकती है, तो आप उपयोगकर्ता को इसके बारे में उचित रूप से बता सकते हैं:
"Error: could not find the file 'goodluckfindingthisfile'"
चूँकि आपका अवैध रो नंबर आंतरिक बग के कारण और उपयोगकर्ता की कोई गलती के कारण नहीं था, इसलिए वास्तव में कोई उपयोगी जानकारी नहीं है जो आप उन्हें दे सकते हैं। यदि आपका ऐप रनटाइम अपवादों को कंसोल के माध्यम से गिरने नहीं देता है, तो संभवतः यह उन्हें कुछ बदसूरत संदेश दे रहा है जैसे:
"Internal error occured: IllegalArgumentException in ...."
संक्षेप में, यदि आपको नहीं लगता है कि आपका क्लाइंट प्रोग्रामर आपके अपवाद को इस तरह से समझा सकता है जो उपयोगकर्ता की मदद करता है, तो आपको शायद एक चेक किए गए अपवाद का उपयोग नहीं करना चाहिए।
तो वे मेरे नियम हैं। कुछ हद तक विरोधाभास है, और इसमें शक नहीं होगा अपवाद (कृपया मेरी मदद करें यदि आप उन्हें परिष्कृत करेंगे)। लेकिन मेरा मुख्य तर्क यह है कि ऐसे मामले हैं FileNotFoundException
जहां जाँच किए गए अपवाद पैरामीटर प्रकार के रूप में एपीआई अनुबंध का एक महत्वपूर्ण और उपयोगी हिस्सा है। इसलिए हमें इसका दुरुपयोग नहीं करना चाहिए क्योंकि इसका दुरुपयोग होता है।
क्षमा करें, इसका मतलब यह नहीं है कि यह इतनी लंबी और भयावह है। मुझे दो सुझावों के साथ समाप्त करने दें:
ए: एपीआई प्रोग्रामर: उनकी उपयोगिता को संरक्षित करने के लिए जाँच किए गए अपवादों का उपयोग करें। जब संदेह में एक अनियंत्रित अपवाद का उपयोग करें।
बी: ग्राहक प्रोग्रामर: अपने विकास में जल्दी से एक लिपटे अपवाद (इसे गूगल करें) बनाने की आदत डालें। JDK 1.4 और बाद में इसके लिए एक कंस्ट्रक्टर प्रदान RuntimeException
करें, लेकिन आप आसानी से अपना खुद का भी निर्माण कर सकते हैं। यहाँ निर्माता है:
public RuntimeException(Throwable cause)
फिर जब भी आपको एक अपवाद को संभालना पड़े, तो आप इसकी आदत में पड़ जाएं और आप आलसी महसूस कर रहे हैं (या आपको लगता है कि एपीआई प्रोग्रामर पहले स्थान पर चेक किए गए अपवाद का उपयोग करने में अति उत्साही था), केवल अपवाद को न निगलें, इसे लपेटें और इसे फिर से उखाड़ फेंकें।
try {
overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
throw new RuntimeException(exception);
}
इसे अपने IDE के छोटे कोड टेम्प्लेट में से एक में रखें और जब आप आलसी महसूस कर रहे हों तो इसका उपयोग करें। इस तरह अगर आपको वास्तव में जाँच किए गए अपवाद को संभालने की आवश्यकता है, तो आपको रनटाइम पर समस्या को देखने के बाद वापस आने और उससे निपटने के लिए मजबूर होना पड़ेगा। क्योंकि, मेरा विश्वास करो (और एंडर्स हेजेल्सबर्ग), आप कभी भी उस TODO में वापस आने वाले नहीं हैं
catch (Exception e) { /* TODO deal with this at some point (yeah right) */}