Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เชี่ยวชาญ Ruby Exception Handling: เทคนิคฉลาดสำหรับ Selective Rescue

หากเคยทำงานกับ Ruby exceptions รู้ว่าระบุได้ว่า exception ไหนถูก rescue ไหนไม่:

begin
  raise ArgumentError
rescue ArgumentError
  # Rescue `ArgumentError`
end

...และน่าจะรู้ว่า rescue "parent" จะ rescue "children" ทั้งหมดด้วย

begin
  raise ArgumentError
rescue StandardError
  # Rescue `ArgumentError` เพราะมันสืบทอดจาก `StandardError`
end

พูด "parent" และ "child" หมายถึง class inheritance ที่ลึกใน Ruby source code มีอย่างนี้:

class ArgumentError < StandardError
  ...
end

เทคนิคน่าสนใจ

คำถาม: Ruby รู้ยังไงว่า exception ใดๆ สืบทอดจากคลาสที่ระบุ?

วิชีวมากที่สุดคือใช้เมธอด is_a? หรือ kind_of? ทưởng像เป็น:

if the_exception.is_a?(StandardError)
  # do the rescue
end

แต่ไม่ได้เป็นอย่างนั้น Ruby ใช้ตัวดำเนินการ === ที่น่าสนใจกว่า

if StandardError === the_exception
  # do the rescue
end

หากไม่เคยใช้ a === b มันมักตอบคำถาม "a อยู่ในกลุ่มที่ b กำหนดหรือไม่?" ตัวอย่าง:

(1..10) === 5        # true
('a'..'f') === "z"    # false
String === "hello"    # true
String === 1          # false
/[0-9]{3}/ === "hello123" # true
/[0-9]{3}/ === "hello"    # false

เพราะ === เป็นเมธอด Ruby ธรรมดาเหมือน == เราสามารถ define เองได้:

class RedThings
  def self.===(thing)
    thing.color == :red
  end
end

สิ่งที่เรารู้: rescue ใช้ === กำหนดว่า exception ไหนถูก rescue และเรา define === เองได้ หมายถึงเราสร้างคลาสที่ตัดสินใจ on-the-fly ว่า exception ไหนถูก rescue:

class SevereMatcher
  def self.===(exception)
    exception.message =~ /severe/
  end
end

begin
  raise RuntimeError, "Something severe happened"
rescue SevereMatcher
  # rescue ทุก exceptions ที่มีคำว่า "severe" ใน message
  # ไม่สนใจ class
end

รู้เทคนิคนี้แล้ว ขีดจำกัดคือจินตนาการ

สรุป

ยอมรับ: อาจไม่ต้องสร้าง dynamic exception matcher แต่ตัวอย่างน่าสนใจว่า implementation detail เล็กๆ อย่างใช้ === แทน kind_of? ทำให้ Ruby ยืดหยุ่นและน่าสนใจมากขึ้น