ruby - रुबी में अपवाद=> ई` को बचाने के लिए यह बुरी शैली क्यों है?




exception-handling (4)

रयान डेविस की रूबी क्विकरफ कहते हैं (स्पष्टीकरण के बिना):

अपवाद को बचाओ मत। कभी। या मैं तुम्हें दबा दूंगा।

क्यों नहीं? क्या करने के लिए सही बात है?


क्योंकि यह सभी अपवादों को कैप्चर करता है। यह असंभव है कि आपका प्रोग्राम उनमें से किसी से भी ठीक हो सकता है।

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

निगलने के अपवाद खराब हैं, ऐसा मत करो।


मान लें कि आप एक कार में हैं (रुबी चल रहे हैं)। आपने हाल ही में ओवर-द-एयर अपग्रेड सिस्टम (जो eval का उपयोग करता है) के साथ एक नया स्टीयरिंग व्हील स्थापित किया है, लेकिन आप सिंटैक्स पर गड़बड़ वाले प्रोग्रामर में से एक को नहीं जानते थे।

आप एक पुल पर हैं, और महसूस करते हैं कि आप रेलिंग की ओर थोड़ा सा जा रहे हैं, इसलिए आप बाएं मुड़ें।

def turn_left
  self.turn left:
end

उफ़! शायद यह अच्छा नहीं है , सौभाग्य से, रूबी एक SyntaxError उठाती है।

कार तुरंत बंद होनी चाहिए - है ना?

नहीं।

begin
  #...
  eval self.steering_wheel
  #...
rescue Exception => e
  self.beep
  self.log "Caught #{e}.", :warn
  self.log "Logged Error - Continuing Process.", :info
end

बीप बीप

चेतावनी: सिंटेक्स त्रुटि अपवाद पकड़ा।

जानकारी: लॉग इन त्रुटि - निरंतर प्रक्रिया।

आप देखते हैं कि कुछ गलत है, और आप आपातकालीन ब्रेक पर स्लैम ( ^C : Interrupt )

बीप बीप

चेतावनी: इंटरप्ट अपवाद पकड़ा।

जानकारी: लॉग इन त्रुटि - निरंतर प्रक्रिया।

हाँ - इससे ज्यादा मदद नहीं मिली। आप रेल के बहुत करीब हैं, इसलिए आप कार को पार्क में kill हैं (आईएनजी: SignalException )।

बीप बीप

चेतावनी: सिग्नल अपवाद अपवाद पकड़ा।

जानकारी: लॉग इन त्रुटि - निरंतर प्रक्रिया।

आखिरी दूसरे में, आप चाबियाँ ( kill -9 ) खींचते हैं, और कार रुक जाती है, आप स्टीयरिंग व्हील में आगे बढ़ते हैं (एयरबैग फुला नहीं सकता है क्योंकि आपने प्रोग्राम को गर्व से नहीं रोका - आपने इसे समाप्त कर दिया) और आपकी कार के पीछे वाला कंप्यूटर इसके सामने सीट में घूमता है। कागजात पर कोक का आधा भरा कैन फैलता है। पीठ में किराने का सामान कुचल दिया जाता है, और अधिकांश अंडे की जर्दी और दूध में ढके होते हैं। कार को गंभीर मरम्मत और सफाई की जरूरत है। (डेटा हानि)

उम्मीद है कि आपके पास बीमा (बैकअप) है। ओह हाँ - क्योंकि एयरबैग फुलाया नहीं था, आप शायद चोट पहुंच रहे हैं (निकाल दिया, आदि)।

लेकिन रुकें! वहाँ है अधिक कारण आप rescue Exception => e का उपयोग क्यों कर सकते हैं!

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

 begin 
    # do driving stuff
 rescue Exception => e
    self.airbags.inflate if self.speed >= 5.mph 
    raise
 end

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

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

शुक्र है, रुबी बहुत बढ़िया है, आप केवल ensure कीवर्ड का उपयोग कर सकते हैं, जो सुनिश्चित करता है कि कोड चलता है। ensure कीवर्ड कोड को चलाएगा चाहे कोई फर्क नहीं पड़ता - अगर कोई अपवाद फेंक दिया जाता है, यदि कोई नहीं है, तो दुनिया को समाप्त होने पर एकमात्र अपवाद (या अन्य असंभव घटनाएं) होती हैं।

 begin 
    # do driving stuff
 ensure
    self.airbags.inflate if self.speed >= 5.mph 
 end

बूम! और वह कोड वैसे भी चलाना चाहिए। rescue Exception => e का उपयोग करने का एकमात्र कारण यह है कि यदि आपको अपवाद तक पहुंच की आवश्यकता है, या यदि आप केवल अपवाद पर कोड चलाने के लिए चाहते हैं। और त्रुटि को फिर से उठाना याद रखें। हर बार। या आपके पास 3 लोग आपको छेड़छाड़ करेंगे (आपके मालिक सहित)।

टी एल; डॉ

rescue Exception => e (और अपवाद को फिर से उठाएं नहीं) को बचाएं - या आप एक पुल को ड्राइव कर सकते हैं


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


असली नियम है: अपवादों को फेंक न दें। आपके उद्धरण के लेखक की निष्पक्षता संदिग्ध है, जैसा कि इस तथ्य से प्रमाणित है कि यह समाप्त होता है

या मैं तुम्हें दबा दूंगा

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

#! /usr/bin/ruby

while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
end

नहीं, वास्तव में, ऐसा मत करो। यह देखने के लिए कि यह काम करता है या नहीं।

हालांकि, कहें कि आपके पास थ्रेडेड सर्वर है और आप सभी अपवादों को नहीं चाहते हैं:

  1. अनदेखा किया जाना चाहिए (डिफ़ॉल्ट)
  2. सर्वर को रोकें (जो होता है अगर आप thread.abort_on_exception = true कहते हैं)।

फिर यह आपके कनेक्शन हैंडलिंग थ्रेड में पूरी तरह से स्वीकार्य है:

begin
  # do stuff
rescue Exception => e
  myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
    myLogger.error("Stack trace: #{backtrace.map {|l| "  #{l}\n"}.join}")
end

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

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

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

ध्यान दें कि एक और रूबी मुहावरे है जिसका बहुत अधिक प्रभाव है:

a = do_something rescue "something else"

इस पंक्ति में, यदि do_something अपवाद उठाता है, तो यह रूबी द्वारा पकड़ा जाता है, फेंक दिया जाता है, और उसे "something else" सौंपा जाता है।

आम तौर पर, ऐसा न करें, विशेष मामलों को छोड़कर जहां आप जानते हैं कि आपको चिंता करने की आवश्यकता नहीं है। एक उदाहरण:

debugger rescue nil

debugger फ़ंक्शन आपके कोड में ब्रेकपॉइंट सेट करने का एक अच्छा तरीका है, लेकिन यदि डीबगर और रेल के बाहर चल रहा है, तो यह अपवाद उठाता है। अब सैद्धांतिक रूप से आपको अपने प्रोग्राम में चारों ओर झूठ बोलने वाले डीबग कोड नहीं छोड़ना चाहिए (पीएफ! कोई भी ऐसा नहीं करता है!) लेकिन आप इसे किसी कारण से थोड़ी देर तक रखना चाहेंगे, लेकिन लगातार अपने डीबगर को नहीं चला सकते।

ध्यान दें:

  1. यदि आपने किसी और के प्रोग्राम को चलाया है जो सिग्नल अपवादों को पकड़ता है और उन्हें अनदेखा करता है, (उपरोक्त कोड कहें) तो:

    • लिनक्स में, एक खोल में, pgrep ruby टाइप करें, या ps | grep ruby ps | grep ruby , अपने अपमानजनक कार्यक्रम के पीआईडी ​​की तलाश करें, और उसके बाद kill -9 <PID> चलाएं।
    • विंडोज़ में, टास्क मैनेजर ( CTRL - SHIFT - ESC ) का उपयोग करें, "प्रक्रियाओं" टैब पर जाएं, अपनी प्रक्रिया ढूंढें, राइट क्लिक करें और "एंड प्रोसेस" चुनें।
  2. यदि आप किसी और के कार्यक्रम के साथ काम कर रहे हैं, जो किसी भी कारण से, इन अनदेखा-अपवाद ब्लॉक के साथ मसालेदार है, तो इसे मुख्य रेखा के शीर्ष पर डालना एक संभावित पुलिस-आउट है:

    %W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
    

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

  3. अगर आपको ऐसा करने की ज़रूरत है:

    begin
      do_something
    rescue Exception => e
      critical_cleanup
      raise
    end
    

    आप वास्तव में ऐसा कर सकते हैं:

    begin
      do_something
    ensure
      critical_cleanup
    end
    

    दूसरे मामले में, हर बार critical cleanup को बुलाया जाएगा, चाहे कोई अपवाद फेंक दिया गया हो या नहीं।





exception-handling