Ruby Refinement
เป็นที่รู้กันดีว่า Ruby ถูกออกมาแบบมาด้วยหลักการ Open/Closed ซึ่งก็ทำให้เราสามารถที่จะทำ Monkey Patching ให้กับคลาสหรือโมดูลได้ง่ายดาย แต่อย่างไรก็ตาม Monkey Patching ก็เป็นดาบสองคม เพราะการ patch จะส่งผลต่อทุกๆ instance ของคลาส ซึ่งถ้าเป็นการเพิ่มเมธอดใหม่ก็ดูจะไม่กระทบอะไรเท่าไหร่ แต่ถ้าเป็นการ override เมธอดเดิมที่มีอยู่ ก็จะทำให้เกิดบั๊คกับโค้ดเราได้
message.rb
class Message
def initailize(message)
@message = message
end
def print
puts @message
end
end
patched_message.rb (Monkey Patching)
class Message
def print
puts "[Patched]: #{@message}"
end
end
app.rb (Before Patch)
require_relative './message'
msg = Message.new "Great Day"
msg.print # --> Great Day
msg = Message.new('Awesome Day')
msg.print # --> Awesome Day
app.rb (After Patch)
require_relative './message'
require_relative './patched_message'
msg = Message.new "Great Day"
msg.print # --> [Patched]: Great Day
msg = Message.new('Awesome Day')
msg.print # --> [Patched]: Awesome Day
ทันที่ที่เรียก Monkey Patching การแสดงผลของทุกๆ instance ก็จะเปลี่ยนไปเมื่อเรียกใช้ print
ที่นี้แหละ Refinement จึงได้กลายมาเป็นพระเอก ที่จะเข้ามาแก้ไขปัญหาข้างต้น โดย Refinement จะคล้ายๆ กับ Moneky Patching แต่จะส่งผลกระทบกับโค้ดที่มีเรียกใช้งานเท่านั้น หรืออยู่ภายใต้ scope ที่เราต้องการนั้นเอง
refined_message.rb
module RefinedMessage
refine Message do
def print
puts "[Refined]: #{@message}"
end
end
end
การนิยาม refinement ให้กับคลาสใดๆ จะต้องอยู่ภายใต้โมดูล
app.rb (Using Refinement)
require_relative './message'
require_relative './refinement_message.rb'
msg = Message.new('Great Day')
msg.print # --> Great Day
using RefinedMessage
msg = Message.new('Awesome Day')
msg.print # --> [Patched]: Awesome Day
การใช้งาน refinement ก็จะใช้
using
ตามด้วยโมดูลที่เราได้นิยาม refinement เอาไว้
สำหรับการใช้ Refinement ก็ประมาณนี้ก็จะช่วยให้เราเพิ่มความสามารถให้กับคลาสเดิมของเราได้ โดยที่ไม่ส่งผลกระทบกับโค้ดเดิมที่เราใช้งานอยู่