2009-03-02 17 views
43

मैं बस कुछ कोड है कि इस तरह देखा समस्या से निपटने के लिए बहुत दर्दनाक समस्या निवारण अनुभव था:एक जावा विरोधी विरोधी पैटर्न को पकड़ने के बिना आखिरकार ब्लॉक है?

try { 
    doSomeStuff() 
    doMore() 
} finally { 
    doSomeOtherStuff() 
} 

क्योंकि doSomeStuff() एक अपवाद है, जो बारी में doSomeOtherStuff वजह से फेंक दिया समस्या के निवारण के लिए बहुत मुश्किल था() भी फेंक एक अपवाद। दूसरा अपवाद (अंततः ब्लॉक द्वारा फेंक दिया गया) मेरे कोड पर फेंक दिया गया था, लेकिन इसमें पहले अपवाद (डूसमस्टस्ट() से फेंक दिया गया) पर एक संभाल नहीं था, जो समस्या का असली मूल कारण था।

कोड इस बजाय, समस्या तत्काल स्पष्ट हो गया होता ने कहा था कि यदि:

try { 
    doSomeStuff() 
    doMore() 
} catch (Exception e) { 
    log.error(e); 
} finally { 
    doSomeOtherStuff() 
} 

तो, मेरे सवाल यह है:

एक अंत में किसी भी कैच ब्लॉक एक अच्छी तरह से बिना इस्तेमाल किया खंड है ज्ञात जावा विरोधी पैटर्न? (यह निश्चित रूप से स्पष्ट रूप से जाने-माने विरोधी पैटर्न का एक स्पष्ट रूप से स्पष्ट उप-वर्ग नहीं है "अपवादों को घुमाओ मत!")

+0

[सी # में पकड़ने के बिना प्रयास करें] पर एक समान प्रश्न भी देखें (http://stackoverflow.com/questions/128818/why-is-try-finally-good-try-catch-bad) - एक ही तर्क लागू होते हैं। – avandeursen

उत्तर

61

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

+0

लेकिन इस मामले में, रूट कारण अपवाद को बुलबुला करने की अनुमति नहीं थी, क्योंकि आखिरकार इसे घुमाया गया था (इस प्रकार इसे गब्बल किया जा रहा था।) – Jared

+0

ठीक है, लेकिन यह एक कोने का मामला है। आप पूछ रहे हैं कि आखिर में कोशिश करें w/o पकड़ सामान्य रूप से एक विरोधी पैटर्न है। – dsimcha

+1

सहमत, dsimcha। मैं उम्मीद कर रहा हूं कि सर्वसम्मति है कि आम तौर पर, यह एक विरोधी पैटर्न नहीं है। शायद एक विरोधी पैटर्न है, "अंत में अपवाद फेंकने ब्लॉक"? – Jared

0

मैं कहूंगा कि कैच ब्लॉक के बिना एक कोशिश ब्लॉक एक विरोधी- पैटर्न। "बिना किसी पकड़ के अंत में" न कहें "एक पकड़ के बिना प्रयास न करें" का उप-समूह है।

+0

मैं सहमत हूं, मुझे नहीं लगता कि यह क्यों कम किया जाएगा। – BobbyShaftoe

+2

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

+0

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

10

अंत में कोई अंतराल नहीं है और कोई पकड़ नहीं है। निम्नलिखित पर विचार करें: एक अपवाद फेंका और है

InputStream in = null; 
try { 
    in = new FileInputStream("file.txt"); 
    // Do something that causes an IOException to be thrown 
} finally { 
    if (in != null) { 
     try { 
      in.close(); 
     } catch (IOException e) { 
      // Nothing we can do. 
     } 
    } 
} 

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

+0

यदि आप इस उदाहरण से पकड़ (अंत में घोंसला) हटाते हैं, तो यदि प्रयास IOException फेंकता है, और बंद() भी करता है, तो आप स्टैक ट्रेस से नहीं बता सकते हैं कि समस्या का मूल कारण है मूल IOException। क्या यह उनमें से एक है, "हाँ, यह हमेशा कठिन होगा" मामले? – Jared

+1

@ जेरेड: हाँ, यह हमेशा कठिन होगा। :) –

+0

एफवाईआई - जब आपने आखिरकार ब्लॉक से पकड़ हटा दी, तो आपने मूल रूप से कहा कि आपको इस बात की परवाह नहीं है कि अपवाद कहां से आया था ... –

26

मुझे लगता है कि असली "एंटी-पैटर्न" यहां finally ब्लॉक में कुछ कर रहा है जो पकड़ नहीं सकता है, पकड़ नहीं सकता है।

1

मैं निम्नलिखित रूप में ट्राई/अंत में उपयोग करें:

try{ 
    Connection connection = ConnectionManager.openConnection(); 
    try{ 
     //work with the connection; 
    }finally{ 
     if(connection != null){ 
      connection.close();   
     } 
    } 
}catch(ConnectionException connectionException){ 
    //handle connection exception; 
} 

मैं (अंत में में + नेस्टेड ट्राई/कैच) ट्राई/कैच/अंत में करने के लिए इस पसंद करते हैं। मुझे लगता है कि यह अधिक संक्षिप्त है और मैं पकड़ (अपवाद) को डुप्लिकेट नहीं करता हूं।

+0

क्या अंततः गोबलिंग में अपवाद की एक ही समस्या से पीड़ित नहीं है कोशिश में एक अपवाद? – Jared

1
try { 
    doSomeStuff() 
    doMore() 
} catch (Exception e) { 
    log.error(e); 
} finally { 
    doSomeOtherStuff() 
} 

है कि या तो ... तुम सिर्फ अधिक बग छिपा रखा (अच्छी तरह से नहीं वास्तव में उन्हें छिपा दिया ... लेकिन यह कठिन उनसे निपटने के लिए किए गए मत करो। जब आप अपवाद आप भी किसी भी प्रकार का पकड़ने रहे हैं पकड़ने RuntimeException (NullPointer और ArrayIndexOutOfBounds की तरह)

सामान्य तौर पर, अपवाद आप को पकड़ने के लिए (अपवाद चेक) और परीक्षण समय RuntimeExceptions प्रोग्रामर त्रुटियों के लिए इस्तेमाल किया जा करने के लिए डिज़ाइन कर रहे हैं पर अन्य लोगों के साथ सौदा किया है पकड़ने -।। और प्रोग्रामर त्रुटियों बातें हैं जो ठीक से डीबग किए गए प्रोग्राम में नहीं होना चाहिए।

+0

हाँ ... सहमत हुए। यह वास्तव में वास्तविक विरोधी पैटर्न को इंगित करता है जो आखिरकार अपवाद फेंकता है। – Jared

-1

मुझे लगता है कि पकड़ने की कोशिश विरोधी पैटर्न है। असाधारण स्थितियों (फ़ाइल आईओ त्रुटियों, सॉकेट टाइमआउट, आदि) को संभालने के लिए प्रयास/पकड़ का उपयोग करना एक विरोधी पैटर्न नहीं है।

यदि आप सफाई के लिए प्रयास/आखिरकार उपयोग कर रहे हैं, तो इसके बजाय एक उपयोग ब्लॉक पर विचार करें।

16

बिलकुल नहीं।

अंत में कोड क्या गलत है।

याद रखें कि आखिरकार हमेशा निष्पादित हो जाएगा, और यह केवल कुछ जोखिम भरा है (जैसा कि आपने अभी देखा है) जो कुछ अपवाद फेंक सकता है।

5

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

एक बात मैं कर जब फ़ाइल हैंडल (लेखन के लिए) के साथ काम कर IOUtils.closeQuietly विधि है, जो अपवाद फेंक नहीं है का उपयोग कर इसे बंद करने से पहले धारा निस्तब्धता है:

 

OutputStream os = null; 
OutputStreamWriter wos = null; 
try { 
    os = new FileOutputStream(...); 
    wos = new OutputStreamWriter(os); 
    // Lots of code 

    wos.flush(); 
    os.flush(); 
finally { 
    IOUtils.closeQuietly(wos); 
    IOUtils.closeQuietly(os); 
} 
 

इसे करने में मुझे अच्छा लगता है कि निम्नलिखित कारणों के लिए रास्ता:

  • यह पूरी तरह से सुरक्षित एक अपवाद की अनदेखी करने के लिए जब एक फ़ाइल को बंद करने नहीं है - अगर वहाँ बाइट्स कि फाइल करने के लिए अभी तक नहीं लिखा गया है, तो फ़ाइल राज्य में नहीं हो सकता है फोन करने वाले होगा उम्मीद;
  • तो, यदि फ्लश() विधि के दौरान कोई अपवाद उठाया जाता है, तो यह कॉलर को प्रचारित किया जाएगा लेकिन मैं अभी भी सुनिश्चित कर दूंगा कि सभी फाइलें बंद हैं। विधि IOUtils.closeQuietly (...) कम verbose है तो इसी प्रयास ... पकड़ो ... मुझे ब्लॉक अनदेखा;
  • यदि एकाधिक आउटपुट स्ट्रीम का उपयोग फ्लश() विधि के लिए ऑर्डर महत्वपूर्ण है। रचनाओं के रूप में अन्य धाराओं को पारित करके बनाई गई धाराओं को पहले फ़्लश किया जाना चाहिए। वही बात निकट() विधि के लिए मान्य है, लेकिन फ्लश() मेरी राय में अधिक स्पष्ट है।
1

मेरी राय में, catch किसी प्रकार की समस्या का संकेत देता है। संसाधन मुहावरे बहुत आसान है:

acquire 
try { 
    use 
} finally { 
    release 
} 

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

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

इसलिए, आपके संसाधन को मुहावरे के आसपास निष्पादन के साथ दूर करने का सार है।

0

कोशिश करें/अंत में संसाधनों को ठीक से मुक्त करने का एक तरीका है। अंत में ब्लॉक में कोड कभी फेंकना नहीं चाहिए क्योंकि इसे केवल उन संसाधनों या राज्यों पर कार्य करना चाहिए जो प्रयास ब्लॉक में प्रवेश करने के लिए प्राथमिक प्राप्त हुए थे।

एक तरफ, मुझे लगता है कि log4J लगभग एक विरोधी पैटर्न है।

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

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

0

try-finally मदद मिल सकती है आप एक विधि कई return बयान है मामले में कॉपी-पेस्ट कोड को कम करने के निम्न उदाहरण (एंड्रॉयड जावा) पर विचार करें:।

boolean doSomethingIfTableNotEmpty(SQLiteDatabase db) { 
    Cursor cursor = db.rawQuery("SELECT * FROM table", null); 
    if (cursor != null) { 
     try { 
      if (cursor.getCount() == 0) { 
       return false; 
      } 
     } finally { 
      // this will get executed even if return was executed above 
      cursor.close(); 
     } 
    } 
    // database had rows, so do something... 
    return true; 
} 

अगर वहाँ था finally खंड, आपको दो बार लिखना पड़ सकता है: return false से पहले और आसपास के if खंड के बाद भी।

संबंधित मुद्दे