Monkey Patch 是 Ruby 作為動態語言的一大特色,可以在程式運行時動態修改或擴充程式碼。今年Ruby TW conference 也多次提到這個技巧,當時我想不透Monkey(猴子!?) 跟Ruby的關係,後來才發現是我早已用到的open class
技巧 。
讓我拿最愛的射雕三部曲說明:
首先用@novels
來列舉這三部小說,主要角色輸出的是男角
class Novel
@novels =
[{title: '射雕英雄傳', male: '郭靖', female: '黃蓉'},
{title: '神鵰俠侶', male: '楊過', female: '程英'},
{title: '倚天屠龍記', male: '張無忌', female: '趙敏'}]
def self.main_role
@novels.map{ |novel| novel[:male]}
end
end
Novel.main_role #=> ["郭靖", "楊過", "張無忌"]
Novel.methods.size #=> 189
如果老闆緊急說要改輸出女角,可以在console下這段程式:
class Novel
def self.main_role
@novels.map{ |novel| novel[:female]}
end
end
Novel.main_role #=> ["黃蓉", "程英", "趙敏"]
Novel.methods.size #=> 189
主要角色換成女角了,而Novel
類別方法總數沒變,依然是189個
老闆又說要新增輸出書名方法book_title
老闆怎麼又改需求凸W凸
class Novel
def self.book_title
@novels.map{ |novel| novel[:title]}
end
end
Novel.book_title #=> ["射雕英雄傳", "神鵰俠侶", "倚天屠龍記"]
Novel.methods.size #=> 190
我們可以得到 Ruby 的 Monkey Patch 的特性:
1.不是把原本的程式碼都蓋掉 (像@novels後兩段程式碼沒給,但還是拿得到)
2.相同名字的方法會被替換掉
3.方法名稱不同,就新增一個新方法 (方法數從189變成190)
ps. Monkey Patch 就像把class打開,做完增刪改再蓋起來,又稱為 open class
比方說NewNovel
類別繼承自上面的Novel
類別,但是book_title
方法我想要修改,就可以使用Monkey Patch:
class NewNovel < Novel
@new_novel = [{title: '翻雲覆雨'}, {title: '王道劍'}]
def self.book_title
@new_novel.map{ |novel| "書名是#{novel[:title]}"}
end
end
NewNovel.book_title #=> ["書名是翻雲覆雨", "書名是王道劍"]
也看過其他人分享,想要修改Gem的內容,但又不想直接更動Gem的檔案,找到Gem原始碼的Class或Module跟想修改的方法,再用Monkey Patch去修改。
雖然 MetaProgramming 的技巧很好用,但是會增加debug的困難度,因為你不知道出問題的程式碼到底在哪一段?是原本的程式碼還是動態修補的程式碼? 如果可以還是少用。另外也建議,Monkey Patch 不要散落在專案的各個地方,應該集中放在一個資料夾下面,ex. app/extension
,這樣debug 才會容易些。
豆知識:
猴子補丁這詞是怎麼來的?
游擊補強(Guerrilla patch)是指執行時期動態改變程式碼,因為英文上同音於猩猩修補(Gorilla patch),後來改稱猴子補丁(Monkey patch)聽來比較不嚇人一些。
ps.為什麼神鵰俠侶女角不選小龍女?
因為我更喜歡程英(๑•̀ㅂ•́)و✧