Monkey Patching in Ruby
Monkey Patching คือ Feature หนึ่งของภาษา Ruby
ที่อนุญาติให้เราเพิ่ม หรือ แก้ไข method ของ Class ที่ถูกประกาศไว้แล้วได้ ...
หรืออีกนัยหนึ่งเราอาจบอกได้ว่า เป็นการเพิ่มความสามารถให้ Class ที่มีอยู่แล้วนั่นเอง
หรืออีกนัยหนึ่งเราอาจบอกได้ว่า เป็นการเพิ่มความสามารถให้ Class ที่มีอยู่แล้วนั่นเอง
เช่นบน Rails เนี่ย เขาก็แก้ให้ Fixnum Class (Class ที่จัดการกับตัวเลขใน Ruby)
สามารถเขียน 2.days.ago เพื่อคืนค่าเวลาของ 2 วันที่แล้วได้
ปัญหาที่เกิดขึ้นคือ ในเมื่อใครๆ ก็แก้ได้ เมื่อทำงานกันเป็นทีม ต้องคุยกันให้ดี
ว่าใคร Monkey Patch อะไรไว้บ้าง ... จะได้เกิดการชนกันของ method ที่เราเพิ่มหรือแก้ไป
(ตาม Bug ยากด้วย) ซึ่งใน Ruby 2.0 ก็มี Feature ที่มาแก้ปัญหานี้ได้ด้วยคือ Refinement
ว่าใคร Monkey Patch อะไรไว้บ้าง ... จะได้เกิดการชนกันของ method ที่เราเพิ่มหรือแก้ไป
(ตาม Bug ยากด้วย) ซึ่งใน Ruby 2.0 ก็มี Feature ที่มาแก้ปัญหานี้ได้ด้วยคือ Refinement
แต่ผมจะมาแนะนำ Feature นี้ในเชิงทั่วๆ ไป เช่น การเล่น Problem Solving
ที่ผมใช้ในเชิงว่าผมต้องการ Refactor Code ให้สวยขึ้น อ่านง่ายขึ้น
ที่ผมใช้ในเชิงว่าผมต้องการ Refactor Code ให้สวยขึ้น อ่านง่ายขึ้น
ตัวอย่างง่ายๆ เช่น ปกติถ้าผมต้องการเขียนฟังก์ชั่นที่ใช้ในการตรวจสอบความเป็นพาลินโดรมของ String สักสายนึง ก็จะเขียนแบบนี้
def is_palindrome(s) s == s.reverse end
แล้วเรียกใช้โดยเขียนแบบนี้
s = "racecar" if is_palindrome(s) puts "s is Palindrome" else puts "s is not Palindrome" end
ซึ่งมันก็พอรับได้แหละ แต่เนื่องจากการเป็น Dynamic Typing (ไม่รู้ type ของ s จริงๆ ถ้าส่ง s ที่เป็น Integer ไปก็จะพัง)
เราจะเห็นว่า ถ้ามีฟังก์ชั่นที่ต้องใช้งานเยอะๆ ก็คงจะเละเทะน่าดู และฟังก์ชั่นนี้เขียนมาเพื่อใช้งานกับ String เท่านั้น ผมก็เลยเลือกที่จะ Monkey Patch มัน
วิธีการง่ายๆ ก็คือเขียนบอกว่า เราจะทำ Monkey Patching กับ Class อะไร
จะเพิ่ม method อะไร และแทนตัวแปรที่จะมาเรียกใช้งานด้วย self
class String def is_palindrome self == self.reverse end end
พอเวลาเรียกใช้เราก็เรียกผ่านตัวแปรที่เป็น String ได้ทันทีแบบนี้
s = "abbba" if s.is_palindrome puts "s is Palindrome" else puts "s is not Palindrome" end
อีกตัวอย่างนะครับ คราวนี้ผมจะเขียน method เพื่อตรวจสอบว่าตัวเลขนั้นๆ เป็นจำนวนเฉพาะหรือไม่ ?
class Fixnum def root Math.sqrt(self) end def is_prime return false if self < 2 2.upto(self.root.to_i) { |i| return false if self % i == 0 } return true end end
เวลาใช้งาน อย่างเช่น ต้องการแสดงจำนวนเฉพาะที่อยู่ระหว่าง 1 - 100 ก็เขียนแบบนี้
1.upto(100) do |i| if i.is_prime print "#{i} " end end
ทั้งนี้ สิ่งที่ควรระวังก็เรื่องการตั้งชื่อแหละครับ ถ้ามันไปซ้ำกับ method ที่มีอยู่ก็แปลว่าเราแก้ไข method ที่มีอยู่ และอย่าไปซ้ำกับพวก reserved word ทั้งหลาย ทั้งแหล่ ลองนำไปใช้ดูนะครับ ... ผมว่า Code จะดูสวยและเป็นระเบียบมากขึ้นเลยนะ
Comments
Post a Comment