"कितना बुरा" कोशिश-कैच-अंततः ब्लॉक में असंबंधित कोड है?


11

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

संदर्भित क्यू में, अंत में कोड का उपयोग किए गए संरचना और पूर्व-लाने की आवश्यकता से संबंधित है। मेरा सवाल थोड़ा अलग है, और मेरा मानना ​​है कि यह व्यापक दर्शकों के लिए जर्मे है। मेरा विशेष उदाहरण एक C # winform ऐप है, लेकिन यह C ++ / Java अंत में उपयोग के लिए भी लागू होगा।

मैं काफी कुछ कोशिश कर रहा हूँ, जो अंततः-ब्लॉक को पकड़ने की कोशिश कर रहा हूँ, जहाँ बहुत से कोड असंबंधित हैं अपवाद और अपवाद हैंडलिंग / क्लीनअप को ब्लॉक के भीतर दफन किया गया है। और मैं अपने पूर्वाग्रह को स्वीकार करूंगा कि बहुत तंग कोशिश-कैच-अंततः ब्लॉक के साथ कोड के अपवाद और हैंडलिंग से संबंधित हैं। यहाँ कुछ उदाहरण हैं जो मैं देख रहा हूँ।

कोशिश करें कि ब्लॉक में बहुत सारी प्रारंभिक कॉल और चर होंगे जो उस कोड तक ले जा सकते हैं जो फेंक सकता है। लॉगिंग जानकारी सेटअप में जाएगी और साथ ही कोशिश ब्लॉक में चलेगी।

अंत में ब्लॉक में फॉर्म / मॉड्यूल / कंट्रोल फॉर्मेटिंग कॉल होंगे (ऐप को समाप्त करने के बारे में होने के बावजूद, कैच ब्लॉक में व्यक्त किया गया है), साथ ही पैनल जैसे नए ऑब्जेक्ट भी बना रहे हैं।

मोटे तौर पर:

    मेथडनाम (...)
    {
        प्रयत्न
        {
            // विधि के लिए बहुत सारे कोड ...
            // कोड जो फेंक सकता है ...
            // विधि और वापसी के लिए बहुत सारे कोड ...
        }
        पकड़ (कुछ)
        {// आउटलेट अपवाद}
        आखिरकार
        {
            // कुछ सफाई अपवाद के कारण, चीजों को बंद करना
            // उस सामान के लिए अधिक कोड जो बनाया गया था (अनदेखी कि किसी भी अपवाद को फेंक दिया जा सकता है) ...
            // शायद कुछ और वस्तुओं का निर्माण करें
        }
    }

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

क्या खराब शैली बदलाव करने को सही ठहराती है? क्या इसी तरह की स्थिति से किसी को बुरी तरह से जलाया गया है? क्या आप उस बुरे अनुभव का विवरण साझा करना चाहेंगे? इसे छोड़ दें क्योंकि मैं अति-प्रतिक्रिया कर रहा हूं और यह शैली का बुरा नहीं है? चीजों को बाँधने के रखरखाव लाभ प्राप्त करें?


"C ++" टैग यहां नहीं है, क्योंकि C ++ में नहीं है (और इसकी आवश्यकता नहीं है) finally। सभी अच्छे उपयोग आरएआई / आरआरआईडी / एसबीआरएम (जो भी आपको पसंद हो) के अंतर्गत आते हैं।
डेविड थॉर्नले

2
@DavidThornley: उदाहरण के अलावा जहां 'अंततः' कीवर्ड का उपयोग किया जाता है, शेष प्रश्न C ++ पर पूरी तरह से लागू होता है। मैं एक C ++ डेवलपर हूं और उस कीवर्ड ने वास्तव में मुझे भ्रमित नहीं किया है। और यह देखते हुए कि मेरी अपनी टीम के साथ अर्ध-नियमित आधार पर ऐसी ही बातचीत हो रही है, यह सवाल बहुत ही प्रासंगिक है कि हम C ++ के साथ क्या करते हैं
DXM

@DXM: मुझे यह देखने में परेशानी हो रही है (और मुझे पता है कि finallyC # में क्या होता है)। C ++ समतुल्य क्या है? जो मैं सोच रहा हूं कि कोड के बाद कोड है catch, और जो C # के बाद कोड के लिए समान लागू होता है finally
डेविड थॉर्नले

@DavidThornley: कोड अंत में वह कोड होता है जिसे किसी भी मामले में निष्पादित नहीं किया जाता है
orlp

1
@ नाइटक्रैकर, यह वास्तव में सही नहीं है। यदि आप ऐसा करते हैं तो इसे निष्पादित नहीं किया जाएगा Environment.FailFast(); यदि आपके पास कोई अनकहा अपवाद नहीं है, तो इसे निष्पादित नहीं किया जा सकता है। और यह और भी अधिक जटिल हो जाता है यदि आपके पास एक इट्रेटर ब्लॉक है जिसके साथ finallyआप मैन्युअल रूप से पुनरावृति करते हैं।
svick

जवाबों:


10

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

सबसे पहले, आप ओवरएक्टिंग नहीं कर रहे हैं। यह बुरा कोड है। जैसा आपने कहा, कैच ब्लॉक गर्भपात और रोकने की तैयारी के बारे में होना चाहिए। यह वस्तुओं (विशेष रूप से पैनलों) को बनाने का समय नहीं है। मैं यह भी बताना शुरू नहीं कर सकता कि यह खराब क्यों है।

ऐसा कहे जाने के बाद...

मेरी पहली सलाह है: अगर यह टूटा नहीं है, तो इसे मत छुओ!

यदि आपका काम उस कोड को बनाए रखना है जिसे तोड़ने के लिए आपको अपना सर्वश्रेष्ठ प्रदर्शन करना है। मुझे पता है कि यह दर्दनाक है (मैं वहां गया हूं) लेकिन आपको अपनी पूरी कोशिश करनी होगी कि जो पहले से काम कर रहा है उसे न तोड़ें।

मेरी दूसरी सलाह है: यदि आपको अधिक सुविधाएँ जोड़ना हैं, तो मौजूदा कोड संरचना को यथासंभव रखने का प्रयास करें ताकि आप कोड को न तोड़े।

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

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

मेरी तीसरी सलाह है: यदि आप कोड तोड़ते हैं तो आप जल जाएंगे, इससे कोई फर्क नहीं पड़ता कि यह आपकी गलती है या नहीं।

यदि आपको रखरखाव देने के लिए काम पर रखा गया है तो यह वास्तव में मायने नहीं रखता है अगर आवेदन अलग हो रहा है क्योंकि किसी और ने गलत निर्णय लिया है। उपयोगकर्ता के दृष्टिकोण से यह पहले काम कर रहा था और अब आपने इसे तोड़ दिया। तुमने तोड़ा!

जोएल ने अपने लेख में कई कारणों को बताते हुए बहुत अच्छी तरह से कहा है कि आपको विरासत कोड को फिर से क्यों नहीं लिखना चाहिए।

http://www.joelonsoftware.com/articles/fog0000000069.html

तो आपको उस तरह के कोड के बारे में वास्तव में बुरा महसूस करना चाहिए (और आपको कभी भी ऐसा कुछ भी नहीं लिखना चाहिए) लेकिन इसे बनाए रखना एक अलग राक्षस है।

मेरे अनुभव के बारे में: मुझे लगभग 1 साल तक कोड को बनाए रखना था और आखिरकार मैं इसे स्क्रैच से फिर से लिखने में सक्षम था लेकिन एक बार में ही नहीं।

क्या हुआ कि कोड इतना खराब था कि नई सुविधाओं को लागू करना असंभव था। मौजूदा एप्लिकेशन में गंभीर प्रदर्शन और प्रयोज्य मुद्दे थे। आखिरकार मुझे एक बदलाव करने के लिए कहा गया, जिसमें मुझे 3-4 महीने लगेंगे (ज्यादातर इसलिए कि उस कोड के साथ काम करने से मुझे सामान्य से अधिक समय लग गया)। मुझे लगा कि मैं लगभग 5-6 महीनों में उस पूरे टुकड़े (वांछित नई सुविधा को लागू करने सहित) को फिर से लिख सकता हूं। मैं इस प्रस्ताव को हितधारकों के पास लाया और वे इसे फिर से लिखने के लिए सहमत हुए (सौभाग्य से मेरे लिए)।

मैं इस टुकड़े को फिर से लिखने के बाद उन्हें समझ में आया कि मैं पहले से जो कुछ था उससे ज्यादा बेहतर सामान दे सकता हूं। इसलिए मैं पूरे आवेदन को फिर से लिखने में सक्षम था।

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

कुछ साल बाद और यह एप्लिकेशन पूरी कंपनी द्वारा उपयोग किए जाने वाले एक सुंदर कुंजी उपकरण में बदल गया है। मुझे 100% यकीन है कि यह कभी संभव नहीं हुआ होगा मैंने पूरी बात फिर से नहीं लिखी है।

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


1
+1। लेकिन अंतिम वाक्य एक सीरियल किलर को संदर्भित करता है। क्या यह सचमुच आवश्यक है?
मार्कजे

मुझे इसका एक हिस्सा पसंद है लेकिन एक ही समय में टूटी हुई डिज़ाइनों को छोड़ना और सामान जोड़ना अक्सर एक खराब डिज़ाइन की ओर जाता है। यह बेहतर है कि इसे कैसे ठीक किया जाए और इसे ठीक किया जाए, हालांकि निश्चित रूप से मापा दृष्टिकोण में।
रिकी क्लार्कसन

@RickyClarkson मैं सहमत हूं। यह जानना बहुत कठिन है कि जब आप इसे ओवरडोज़ कर रहे हों (वास्तव में एक रिफलेक्टिंग आवश्यक नहीं है) और जब आप परिवर्तन जोड़कर एप्लिकेशन को वास्तव में नुकसान पहुँचा रहे हों।
एलेक्स

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

@ रिकी क्लार्कसन उस समुदाय के अनुभव का हिस्सा था जिसे मैं टैप करना चाहता था। सभी विरासत कोड को खराब घोषित करना एक समान रूप से खराब विरोधी पैटर्न है। हालांकि, इस कोड में कुछ चीजें हैं जो मैं काम कर रहा हूं, वास्तव में एक अंत में ब्लॉक के हिस्से के रूप में नहीं किया जाना चाहिए। एलेक्स की प्रतिक्रिया और प्रतिक्रिया वही है जिसके बारे में मैं पढ़ना चाह रहा था।

2

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

http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052

जो आपको मूल्यवान संकेत देगा कि कैसे कोड को अधिक परीक्षण योग्य बनाया जा सकता है, यूनिट टेस्ट जोड़ें और नई सुविधाओं को जोड़ने पर कोड को तोड़ने से बचें।

यदि आप इसे सही तरीके से करते हैं, तो आपका तरीका सहज रूप से सरल हो जाएगा, कोशिश-कैच-एंड अंततः गलत स्थानों पर किसी भी असंबंधित कोड को किसी भी अधिक नहीं रखेगा।


2

मान लें कि आपके पास कॉल करने के लिए छह तरीके हैं, जिनमें से चार को केवल तभी कॉल किया जाना चाहिए जब कोई त्रुटि न हो। दो विकल्पों पर विचार करें:

try {
     method1();  // throws
     method2();
     method3();
     method4();
     method5();
 } catch(e) {
     // handle error
 }
 method6();

बनाम

 try {
      method1();  // throws
 }
 catch(e) {
      // handle error
 }
 if(! error ) {
     method2();
     method3();
     method4();
     method5();
 }
 method6();

दूसरे कोड में, आप रिटर्न कोड जैसे अपवादों का इलाज कर रहे हैं। वैचारिक रूप से, यह समान है:

rc = method1();
if( rc != error ) {
     method2();
     method3();
     method4();
     method5();
 }
 method6();

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

पहली जगह में भाषाओं के अपवाद होने का कारण यह है कि रिटर्न कोड के बुरे पुराने दिनों में, हमारे पास अक्सर ऐसी स्थितियाँ होती थीं, जहाँ हमारे पास कई तरीके होते थे जो त्रुटियों को वापस कर सकते थे, और प्रत्येक त्रुटि का मतलब पूरी तरह से सभी विधियों को निरस्त करना था। अपने करियर की शुरुआत में, पुराने C दिनों में, मैंने सभी जगह इस तरह का कोड देखा:

rc = method1();
if( rc != error ) {
     rc = method2();
     if( rc != error ) {
         rc = method3();
         if( rc != error ) {
             rc = method4();
             if(rc != error ) {
                 method5();
             }
         }
     }
 }
 method6();

यह एक अविश्वसनीय रूप से सामान्य पैटर्न था, और एक यह कि अपवाद बहुत करीने से हल करके आपको पहले उदाहरण के ऊपर वापस जाने की अनुमति देता है। एक शैली नियम जो कहता है कि प्रत्येक विधि का अपना अपवाद ब्लॉक है यह पूरी तरह से खिड़की से बाहर फेंकता है। फिर भी परेशान क्यों?

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

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

void method0() : throws MyNewException 
{
    try {
        method1();  // throws MyOtherException
    }
    catch(e) {
        if(e == MyOtherException)
            throw MyNewException();
    }
    method2();
}

यहां, आप केवल परतें जोड़ रहे हैं, और परतें भ्रम को जोड़ते हैं। बस यह करें:

void method0() : throws MyOtherException
{
    method1();
    method2();
}

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

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


स्टीवन - अच्छी तरह से रखी गई प्रतिक्रिया के लिए धन्यवाद, और मैं आपके विचारों से पूरी तरह सहमत हूं। मुझे एक ही कोशिश ब्लॉक में रहने वाले यथोचित संबंधित या अनुक्रमिक प्रकार कोड के साथ कोई समस्या नहीं है। अगर मैं एक वास्तविक कोड स्निपेट पोस्ट करने में सक्षम होता, तो यह पहले फेंकने योग्य कॉल से पहले 5 - 10 पूरी तरह से असंबंधित कॉल दिखाता। एक विशेष रूप से अंत में ब्लॉक बहुत खराब था - कई नए धागे बंद हो रहे थे और अतिरिक्त, गैर-सफाई रूटीन कहा जाता था। और उस मामले के लिए, उस अंत में ब्लॉक के सभी कॉल किसी भी चीज से असंबंधित थे जो फेंक सकते थे।

इस दृष्टिकोण के साथ एक बड़ी समस्या यह है कि यह उन मामलों के बीच कोई अंतर नहीं करता है, जो method0अर्ध-अपेक्षा कर सकते हैं कि एक अपवाद को फेंक दिया जा सकता है, और जहां यह नहीं है। उदाहरण के लिए, मान लें method1कि कभी-कभी InvalidArgumentExceptionइसे फेंक दिया जा सकता है , लेकिन यदि ऐसा नहीं होता है, तो यह एक वस्तु को एक अस्थायी रूप से अमान्य स्थिति में डाल देगा method2, जिसे सही कर दिया जाएगा , जिससे यह उम्मीद की जाती है कि वह वस्तु को एक वैध स्थिति में वापस लाए। यदि मेथड 2 फेंकता है InvalidArgumentException, तो ऐसा कोड जो इस तरह के अपवाद को पकड़ने की उम्मीद method1करता है, वह इसे पकड़ लेगा, भले ही ...
सुपरकैट

... सिस्टम राज्य कोड की अपेक्षाओं से मेल खाएगा। यदि Method1 से एक अपवाद को फेंक दिया जाए, तो Method2 द्वारा फेंके गए एक से अलग तरीके से संभाला जा सकता है, उन्हें लपेटे बिना समझदारी से कैसे पूरा किया जा सकता है?
सुपरकैट

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