今天我想用一個 Codewars 的題目來練練上一章的所學,題目及說明在以下程式碼裡:
class EventEmitter
# your code
end
evt = EventEmitter.new
# 無註冊事件(未使用 on 方法直接 trigger)
evt.trigger('marry_him') # 無任何輸出
# 註冊一個事件
evt.on('marry_me') { puts '好'}
evt.trigger('marry_me') # 印出 好
# 重複註冊事件
evt.on('ithome') { puts '鐵人賽'}
evt.on('ithome') { puts '一天一篇'}
evt.on('ithome') { puts 'hold 住!'}
evt.trigger('ithome') # 印出 鐵人賽 一天一篇 hold 住!
這題的目的是要利用 on
這個方法將後面的 block
內容儲存起來,用 trigger
方法來印出儲存的內容。
分析一下題目,因為已經將 block
接在方法後面,所以沒辦法用前一天提到的 Proc way 直接將 block 變成物件再當做參數傳進去存起來,若直接用 yield way 則是會直接執行 block 的內容而不能儲存,所以這裡用 &
方法來試試。
class EventEmitter
def initialize
@event = Hash.new{|hash, key| hash[key] = [] }
end
def on(str, & block)
@event[str] << block
end
def trigger(str)
@event[str].each{ |block| block.call }
end
end
@event
的實體變數,將「事件」作為 key
,它的 value
是一個空陣列用來裝所有 block
。&
,因此我的@event
會長得像這樣 { :str => [ block_1, block_2, block_3 ]}
。call
方法一個一個呼叫。當然解決事情的方法一定不會只有一種,下面我們用 yield + proc 寫寫看:
class EventEmitter
def initialize
@event = Hash.new{|hash, key| hash[key] = [] }
end
def on(str)
@event[str] << Proc.new{ yield }
end
def trigger(str)
@event[str].each{ |block| block.call }
end
end
這裡的差別在於,上一章有提到 yield
就是將 block 的{}
扒掉,因此我在外面又包一個花括號將它還原回 block ,接著用 Proc.new 將之物件化,後續是一樣的,只是在轉換過程中,這裡的範例我會覺得用 &
比較直觀些。
此文同步刊登於CJ-Han的網站