क्या खराब शैली / खतरनाक रिटर्न के बाद काम करने के लिए अंत में क्लॉज का उपयोग किया जाता है?


30

एक Iterator लिखने के भाग के रूप में, मैंने खुद को निम्नलिखित कोड ऑफ कोड (स्ट्रिपिंग एरर हैंडलिंग) लिखा है।

public T next() {
  try {
    return next;
  } finally {
    next = fetcher.fetchNext(next);
  }
}

पढ़ने में थोड़ा आसान लगता है

public T next() {
  T tmp = next;
  next = fetcher.fetchNext(next);
  return tmp;
}

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

अगर यह बुरा है: क्यों? शैली, प्रदर्शन, नुकसान, ...?

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


10
मैं व्यक्तिगत रूप से पहले की तुलना में दूसरे को समझने में सक्षम हो सकता हूं, लेकिन मैं शायद ही कभी finallyब्लॉक का उपयोग करता हूं ।
क्रिश्चियन मान २

2
वापसी वर्तमान = fetcher.fetchNext (वर्तमान); // इस बारे में, उर्फ ​​क्या आपको वास्तव में प्रीफेटिंग की आवश्यकता है?
स्कारफ्रिज

1
@scarfridge उदाहरण कार्यान्वयन के बारे में है Iterator, जहाँ आपको hasNext()काम करने के लिए वास्तव में पूर्व-निर्धारण की आवश्यकता होती है । यह अपने आप का प्रयास करें।
माआर्टिनस

1
@maaartinus मैं इससे अवगत हूं। कभी-कभी hasNext () केवल सही उदाहरण देता है जैसे hailstone अनुक्रम और कभी-कभी अगला तत्व प्राप्त करना निषेधात्मक रूप से महंगा होता है। बाद के मामले में आपको आलसी आरंभीकरण के समान मांग पर ही तत्व प्राप्त करने का सहारा लेना चाहिए।
स्कारफ्रिज

1
@scarfridge आम उपयोग की समस्या यह Iteratorहै कि आपको मूल्य प्राप्त करने की आवश्यकता है hasNext()('क्योंकि यह मौजूद है, तो यह पता लगाने का एकमात्र तरीका है कि यह मौजूद है या नहीं) और इसे next()ओपी की तरह ही वापस कर दें ।
माॅर्टिनस

जवाबों:


18

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

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

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


1
"चतुर" टिप्पणी के लिए +1। यह बहुत ही सटीक रूप से इस उदाहरण को दर्शाता है।

2
अगले () की 'रिटर्न और एडवांस' कार्रवाई ओपीआई द्वारा अपरिवर्तित जावा इटेरियर इंटरफेस पर मजबूर है।
केविन क्लाइन

37

विशुद्ध रूप से एक शैली के दृष्टिकोण से, मुझे लगता है कि ये तीन लाइनें हैं:

T current = next;
next = fetcher.getNext();
return current;

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


2
एक कोशिश / पकड़ / अंत में ब्लॉक भी अतिरिक्त ओवरहेड जोड़ सकता है, मुझे यकीन नहीं है कि अगर javac या JIT संकलक उन्हें बाहर निकाल देगा, भले ही आप एक अपवाद नहीं फेंक रहे हों।
Martijn Verburg

2
@MartijnVerburg हाँ, javac अभी भी अपवाद तालिका में एक प्रविष्टि जोड़ता है और अंत में कोड डुप्लिकेट करता है, भले ही कोशिश ब्लॉक खाली हो।
डैनियल लुबरोव

@ डैनियल - धन्यवाद मैं भी javap निष्पादित करने के लिए बहुत आलसी था :-)
Martijn Verburg

@Martijn Verburg: AFAIK, एक ट्राइ-कैच-फ़िनॉल्ट ब्लॉक अनिवार्य रूप से मुफ़्त है जब तक कोई वास्तविक रेंडर नहीं होता है। यहाँ, javac कोशिश नहीं करता है और कुछ भी अनुकूलित करने की आवश्यकता नहीं है क्योंकि JIT इसका ध्यान रखेगा।
मैार्टिनस

@maaartinus - धन्यवाद जो समझ में आता है - OpenJDK स्रोत कोड को फिर से पढ़ने की जरूरत है :-)
Martijn Verburg

6

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

मुझे इसका उपयोग करने में कोई समस्या नहीं दिखाई देती है जब "कोई अपवाद शामिल नहीं होता है"। यह उपयोग करने के लिए बहुत आम है try...finallyएक के बिना catchजब कोड केवल फेंक कर सकते हैं RuntimeExceptionऔर आप उन्हें निपटने पर योजना नहीं है। कभी-कभी, अंत में सिर्फ एक सुरक्षा है, और आप अपने कार्यक्रम के तर्क के लिए जानते हैं कि कोई अपवाद कभी नहीं फेंका जाएगा (क्लासिक "यह कभी नहीं होना चाहिए" स्थिति)।

नुकसान: tryब्लॉक के अंदर उठाया गया कोई भी अपवाद finallyबॉक रन बना देगा। जो आपको एक असंगत स्थिति में डाल सकता है। इसलिए, यदि आपका रिटर्न स्टेटमेंट कुछ इस तरह था:

return preProcess(next);

और इस कोड ने एक अपवाद उठाया, fetchNextफिर भी चलेगा। OTOH यदि आप इसे कोडित करते हैं जैसे:

T ret = preProcess(next);
next = fetcher.fetchNext(next);
return ret;

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

प्रदर्शन: यह इस तरह के कोड को डिकंपाइल करने के लिए दिलचस्प होगा यह देखने के लिए कि यह हुड के नीचे कैसे काम करता है, लेकिन मुझे जेवीएम के बारे में पता नहीं है यहां तक ​​कि एक अच्छा अनुमान लगाने के लिए ... अपवाद आमतौर पर "असाधारण" होते हैं, इसलिए कोड जो संभालता है उन्हें गति के लिए अनुकूलित करने की आवश्यकता नहीं है (इसलिए आपके कार्यक्रमों के सामान्य नियंत्रण प्रवाह में अपवादों का उपयोग करने की सलाह नहीं है), लेकिन मुझे इसके बारे में पता नहीं है finally


आप वास्तव में इस तरह से उपयोग करने से बचने के लिए एक बहुत अच्छा कारण प्रदान नहीं कर रहे हैं finally? मेरा मतलब है, जैसा कि आप कहते हैं, अवांछित परिणाम होने पर कोड समाप्त हो जाता है; इससे भी बदतर, आप अपवादों के बारे में सोच भी नहीं रहे हैं।
एंड्रेस एफ।

2
सामान्य नियंत्रण प्रवाह की गति अपवाद से निपटने वाले निर्माणों से काफी प्रभावित नहीं होती है। प्रदर्शन जुर्माना केवल तभी भुगतान किया जाता है जब वास्तव में अपवादों को फेंकने और पकड़ने के लिए, न कि जब एक कोशिश ब्लॉक में प्रवेश करते हैं या सामान्य ऑपरेशन में अंततः ब्लॉक में प्रवेश करते हैं।
yfeldblum

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

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

3

शीर्षक वक्तव्य: "... अंत में वापसी के बाद काम करने के लिए खंड ..." गलत है। फ़ंक्शन लौटने से पहले अंत में ब्लॉक होता है। यह वास्तव में वास्तव में पूरे बिंदु है।

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

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


+1, मैं यह भी जोड़ूंगा कि भले ही भाषा की युक्ति यह गारंटी दे कि मूल्य को वापस करने से पहले संग्रहीत किया जाता है, यह संभावित रूप से पाठक की अपेक्षा के खिलाफ जाता है, और इस तरह जब संभव हो तो इसे टाला जाना चाहिए। डिबगिंग कोडिंग से दोगुना कठिन है ...
jmoreno

0

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

और कीड़ा का अन्य कर सकते हैं, कि सामान्य तौर पर, अंत में कुछ उपयोगी कार्रवाई अपवाद फेंक सकते हैं, तो आप वास्तव में गड़बड़ विधि के साथ समाप्त कर सकते हैं।

इनके कारण, इसे पढ़ने वाले किसी व्यक्ति को इन समस्याओं के बारे में सोचना चाहिए, इसलिए इसे समझना कठिन है।

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