在上次的blog裡提了這麼一句:“其實要多態,還不如在對象裡面儲存一個變數,變數裡面是一段程式,運行到那就把這段程式拿出來eval一下,不比建立幾個子類強麼”。有讀者問這個是什麼意思,要我給個例子。其實這個很簡單,只要想清楚所有的反動派都是紙老虎……嗯,我是說,所有的操作都是lambda。然後把操作當資料就好了。當然,建立在一個基礎上:你使用的語言支援方便地把lambda作為一等公民。
class Host
def state= (state)
@state = state
end
def do_something
@state(self)
end
end
host = Host.new
host.state = {|host| p host }
host.do_something
譬如說這是一個用Ruby實現的State模式(或者說是Strategy模式也好,這倆本來就不是很有區別)。Host在@state裡面儲存一個lambda(Ruby的黑話好像是叫closure),回頭要做什麼操作就拿這個出來用。State/Strategy模式用C#/Java的實現,大家都很熟悉,對比一下就明白我的意思了。
為上次的“C#長得太醜了”一文道個歉,沒想到隨便開個玩笑也能引起語言大戰,實在抱歉,各位讀者就當我被班加羅爾的太陽曬昏了頭打胡亂說罷。由此可以看到,中國程式員(或者說,部分中國程式員)確實覺悟甚高,階級鬥爭這根弦繃得很緊……喜歡一種語言固然是沒啥好說的,要是真對這個語言那麼有信心,犯得著聽見三句抱怨就跳起八丈高嗎?
(補充:上面例子中的lambda還可以寫得更漂亮一點,do_something可以寫成這樣
def do_something
@state
end
而這個傳進去的lambda就可以寫成
host.state = { p self }
這個事情就有點奇妙:lambda的context是在它定義的地方,而不是它調用的地方,也就是說這個lambda的self其實並不是host。不過有個tricky的辦法:用caller可以找到這個lambda的調用者,然後把這個caller給mixin成當前context的self,調用完以後再mixin回來……感謝Obie的靈感和徐八x的實現。)