क्या नेस्टेड कोशिश-कैच ब्लॉक का उपयोग करना एक विरोधी पैटर्न है?


95

क्या यह एक एंटीपैटर्न है? यह एक स्वीकार्य अभ्यास है?

    try {
        //do something
    } catch (Exception e) { 
        try {
            //do something in the same line, but being less ambitious
        } catch (Exception ex) {
            try {
                //Do the minimum acceptable
            } catch (Exception e1) {
                //More try catches?
            }
        }
    }

क्या आप हमें इसके लिए केस दे सकते हैं? आप शीर्ष स्तर की पकड़ में हर त्रुटि प्रकार को क्यों नहीं संभाल सकते हैं?
मोरों

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

@LokiAstari- आपका उदाहरण अंत में सेक्शन में एक कोशिश है .. जहाँ कोई पकड़ नहीं है। यह प्रयास अनुभाग में एक नेस्टेड है .. यह अलग है।
मोरों

4
यह एक प्रतिमान क्यों होना चाहिए?

2
+1 के लिए "अधिक प्रयास कैच?"
जोएलफैन

जवाबों:


85

यह कभी-कभी अपरिहार्य होता है, खासकर यदि आपका पुनर्प्राप्ति कोड अपवाद फेंक सकता है।

सुंदर नहीं, लेकिन कभी-कभी कोई विकल्प नहीं होता है।


17
@ मिस्टरस्मिथ - हमेशा नहीं।
Oded

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

5
@ मिस्टरस्मिथ: मैं अनुक्रमिक कोशिशों के लिए नेस्टेड-कैच-नीड्स पसंद करूंगा जो कि फ्लैग वैरिएबल के साथ आंशिक रूप से नियंत्रित होते हैं (यदि वे कार्यात्मक रूप से समान थे)।
FrustratedWithFormsDesigner

31
कोशिश {लेनदेन। com (); } पकड़ {कोशिश {लेन-देन। ट्रोलबैक (); } {{गंभीरलॉगिंग ()} } एक आवश्यक नेस्टेड ट्राई कैच का एक उदाहरण है
थानोस पपथनसीउ

3
कम से कम कैच ब्लॉक को एक विधि में निकालें, दोस्तों! कम से कम इसे पठनीय बनाओ।
श्री कोच

43

मुझे नहीं लगता कि इसका एंटीपैटर्न, बस व्यापक रूप से दुरुपयोग है।

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

लेकिन कई बार आप इसकी मदद नहीं कर सकते।

try{
     transaction.commit();
   }catch{
     logerror();
     try{
         transaction.rollback(); 
        }catch{
         seriousLogging();
        }
   }

इसके अलावा, आपको असफल रोलबैक को इंगित करने के लिए एक अतिरिक्त बूल की आवश्यकता होगी ...


19

तर्क ठीक है - यह कुछ स्थितियों में एक सही दृष्टिकोण की कोशिश कर सकता है, जो खुद असाधारण घटनाओं का अनुभव कर सकता है .... इसलिए यह पैटर्न बहुत अधिक अपरिहार्य है।

हालाँकि मैं कोड को बेहतर बनाने के लिए निम्नलिखित सुझाव दूंगा:

  • आंतरिक प्रयास को प्रतिबिंबित करें ... ब्लॉक को अलग-अलग कार्यों में शामिल करें, जैसे attemptFallbackMethodऔर attemptMinimalRecovery
  • विशेष अपवाद प्रकारों के बारे में अधिक विशिष्ट हो जो पकड़े जा रहे हैं। क्या आप वास्तव में किसी भी अपवाद उपवर्ग की उम्मीद करते हैं और यदि आप वास्तव में उन सभी को उसी तरह से संभालना चाहते हैं?
  • इस बात पर विचार करें कि क्या कोई finallyब्लॉक अधिक अर्थ लगा सकता है - यह आमतौर पर किसी भी चीज़ के लिए होता है जो "संसाधन सफाई कोड" जैसा महसूस करता है

14

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

try {
    // do something
    return;
} catch (Exception e) {
    // fall through; you probably want to log this
}
try {
    // do something in the same line, but being less ambitious
    return;
} catch (Exception e) {
    // fall through again; you probably want to log this too
}
try {
    // Do the minimum acceptable
    return;
} catch (Exception e) {
    // if you don't have any more fallbacks, then throw an exception here
}
//More try catches?

एक बार जब आप इसे इस तरह से तोड़ चुके होते हैं, तो आप इसे एक रणनीति पैटर्न में लपेटने के बारे में सोच सकते हैं।

interface DoSomethingStrategy {
    public void doSomething() throws Exception;
}

class NormalStrategy implements DoSomethingStrategy {
    public void doSomething() throws Exception {
        // do something
    }
}

class FirstFallbackStrategy implements DoSomethingStrategy {
    public void doSomething() throws Exception {
        // do something in the same line, but being less ambitious
    }
}

class TrySeveralThingsStrategy implements DoSomethingStrategy {
    private DoSomethingStrategy[] strategies = {new NormalStrategy(), new FirstFallbackStrategy()};
    public void doSomething() throws Exception {
        for (DoSomethingStrategy strategy: strategies) {
            try {
                strategy.doSomething();
                return;
            }
            catch (Exception e) {
                // log and continue
            }
        }
        throw new Exception("all strategies failed");
    }
}

तो बस का उपयोग करें TrySeveralThingsStrategy, जो एक तरह की समग्र रणनीति है (एक की कीमत के लिए दो पैटर्न!)।

एक विशाल चेतावनी: ऐसा तब तक न करें जब तक कि आपकी रणनीतियाँ पर्याप्त रूप से जटिल न हों, या आप उन्हें लचीले तरीकों से उपयोग करने में सक्षम होना चाहते हैं। अन्यथा, आप अनावश्यक वस्तु-अभिविन्यास के एक विशाल ढेर के साथ सरल कोड की कुछ पंक्ति को ले रहे हैं।


7

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


6

प्रति-प्रति-विरोधी नहीं, बल्कि एक कोड पैटर्न जो बताता है कि आपको रिफ्लेक्टर की आवश्यकता है।

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

अंगूठे का यह नियम रॉबर्ट सी। मार्टिन द्वारा उनकी पुस्तक 'क्लीन कोड' के सुझाव पर आधारित है:

यदि कीवर्ड 'ट्राय' किसी फ़ंक्शन में मौजूद है, तो यह फ़ंक्शन का पहला शब्द होना चाहिए और कैच या अंत में ब्लॉक होने के बाद कुछ भी नहीं होना चाहिए।

"छद्म-जावा" पर एक त्वरित उदाहरण। मान लीजिए कि हमारे पास ऐसा कुछ है:

try {
    FileInputStream is = new FileInputStream(PATH_ONE);
    String configData = InputStreamUtils.readString(is);
    return configData;
} catch (FileNotFoundException e) {
    try {
        FileInputStream is = new FileInputStream(PATH_TWO);
        String configData = InputStreamUtils.readString(is);
        return configData;
    } catch (FileNotFoundException e) {
        try {
            FileInputStream is = new FileInputStream(PATH_THREE);
            String configData = InputStreamUtils.readString(is);
            return configData;
        } catch (FileNotFoundException e) {
            return null;
        }
    }
}

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

public String loadConfigFile(String path) {
    try {
        FileInputStream is = new FileInputStream(path);
        String configData = InputStreamUtils.readString(is);
        return configData;
    } catch (FileNotFoundException e) {
        return null;
    }
}

अब हम पहले भी इसी उद्देश्य के साथ इसका उपयोग करते हैं।

String[] paths = new String[] {PATH_ONE, PATH_TWO, PATH_THREE};

String configData;
for(String path : paths) {
    configData = loadConfigFile(path);
    if (configData != null) {
        break;
    }
}

मुझे आशा है कि वह मदद करेंगे :)


अच्छा उदाहरण। यह उदाहरण सही मायने में कोड को रिफलेक्टर करना है। हालांकि कुछ अन्य समय में, नेस्टेड ट्राइ-कैच आवश्यक है।
लाइनर्र

4

यह निश्चित रूप से कोड पठनीयता को कम कर रहा है। मैं कहूंगा, यदि आपके पास मौका है , तो घोंसले के शिकार की कोशिश से बचें।

यदि आपको ट्राइ-कैच करना है, तो हमेशा एक मिनट रुकें और सोचें:

  • क्या मुझे उनके साथ गठबंधन करने का मौका मिला है?

    try {  
      ... code  
    } catch (FirstKindOfException e) {  
      ... do something  
    } catch (SecondKindOfException e) {  
      ... do something else    
    }
    
  • क्या मुझे बस नेस्टेड हिस्से को एक नई विधि में निकालना चाहिए? कोड बहुत अधिक साफ होगा।

    ...  
    try {  
      ... code  
    } catch (FirstKindOfException e) {  
       panicMethod();  
    }   
    ...
    
    private void panicMethod(){   
    try{  
    ... do the nested things  
    catch (SecondKindOfException e) {  
      ... do something else    
      }  
    }
    

यह स्पष्ट है कि अगर आपको एक ही विधि में तीन-या अधिक स्तरों के घोंसले बनाने पड़ते हैं, तो यह रिफ्लेक्टर के लिए निश्चित समय का संकेत है।


3

मैंने इस पैटर्न को नेटवर्क कोड में देखा है, और यह वास्तव में समझ में आता है। यहाँ मूल विचार है, छद्मकोड में:

try
   connect;
catch (ConnectionFailure)
   try
      sleep(500);
      connect;
   catch(ConnectionFailure)
      return CANT_CONNECT;
   end try;
end try;

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


2

मैंने इस स्थिति को इस तरह हल किया (कमबैक के साथ प्रयास करें):

$variableForWhichINeedFallback = null;
$fallbackOptions = array('Option1', 'Option2', 'Option3');
while (!$variableForWhichINeedFallback && $fallbackOptions){
    $fallbackOption = array_pop($fallbackOptions);
    try{
        $variableForWhichINeedFallback = doSomethingExceptionalWith($fallbackOption);
    }
    catch{
        continue;
    }
}
if (!$variableForWhichINeedFallback)
    raise new ExceptionalException();

2

मैंने इसे एक टेस्ट क्लास में संयोगवश (JUnit) में करने के लिए "किया" है, जहाँ setUp () विधि में एक अपवाद को फेंकने वाले कंस्ट्रक्टर में अमान्य कंस्ट्रक्टर पैरामीटर के साथ ऑब्जेक्ट्स बनाना था।

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

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


0

मुझे वास्तव में लगता है कि यह एक एंटीपैटर्न है।

कुछ मामलों में आप एक से अधिक ट्राइ-कैच चाहते हैं, लेकिन केवल अगर आप नहीं जानते कि आप किस तरह की त्रुटि देख रहे हैं, जैसे:

public class Test
{
    public static void Test()
    {            
        try
        {
           DoOp1();
        }
        catch(Exception ex)
        {
            // treat
        }

        try
        {
           DoOp2();
        }
        catch(Exception ex)
        {
            // treat
        }

        try
        {
           DoOp3();
        }
        catch(Exception ex)
        {
            // treat
        }
    }

    public static void Test()
    {
        try
        {
            DoOp1();
            DoOp2();
            DoOp3();
        }
        catch (DoOp1Exception ex1)
        {
        }
        catch (DoOp2Exception ex2)
        {
        }
        catch (DoOp3Exception ex3)
        {
        }
    }
}

यदि आप नहीं जानते हैं कि आप जिस चीज की तलाश कर रहे हैं वह पहले तरीके का उपयोग करना है, जो कि IMHO है, बदसूरत और कार्यात्मक नहीं है। मुझे लगता है कि बाद बहुत बेहतर है।

इसलिए, यदि आप जानते हैं कि आप किस प्रकार की त्रुटि की तलाश कर रहे हैं, तो विशिष्ट होना चाहिए । समान विधि के अंदर नेस्टेड या मल्टीपल ट्राइ-कैच की आवश्यकता नहीं है।


2
आपके द्वारा दिखाए गए कोड की तरह वास्तव में सभी मामलों में कोई मतलब नहीं है। हालांकि, ओपी नेस्टेड ट्राई-कैच का जिक्र कर रहा था , जो कि लगातार कई बयानों से काफी अलग सवाल है।
जिमीबी

0

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


0

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

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

उदाहरण के लिए :

String str=null;
try{
   str = method(a);
}
catch(Exception)
{
try{
   str = doMethod(a);
}
catch(Exception ex)
{
  throw ex;
}

यहाँ उपरोक्त उदाहरण विधि में अपवाद फेंकता है लेकिन doMethod (विधि अपवाद को संभालने के लिए उपयोग किया जाता है) यहां तक ​​कि अपवाद को भी फेंक देते हैं। इस मामले में हमें ट्रायल कैच के भीतर ट्राई कैच का उपयोग करना होगा।

कुछ काम जो करने का सुझाव दिया जाता है वह नहीं है ..

try 
{
  .....1
}
catch(Exception ex)
{
}
try 
{
  .....2
}
catch(Exception ex)
{
}
try 
{
  .....3
}
catch(Exception ex)
{
}
try 
{
  .....3
}
catch(Exception ex)
{
}
try 
{
  .....4
}
catch(Exception ex)
{
}

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