昨天我們學到了 Refinement 如何利用 using 關鍵字,來控制覆寫方法可影響的範圍。
今天要介紹 Refinement 的另一項特性將可以取代 Around Aliases - super。
看看以下例子:
class MyClass
def my_method(change = false)
"original my_method()"
end
def another_method
my_method
end
end
module MyClassRefinement
refine MyClass do
def my_method(change)
change ? super : "refined my_method()"
end
end
end
using MyClassRefinement
self # main
MyClass.new.my_method(true) # => "original my_method()"
MyClass.new.my_method(false) # => "refined my_method()"
MyClass.new.another_method # => "original my_method()"
如果將 super 關鍵字放在重新定義後的方法內,就可以利用 super 取得原始舊版的方法。
再來一個範例:
module StringRefinement
refine String do
def length
super > 5 ? 'long' : 'short'
end
end
end
using StringRefinement
"War and Peace".length # => "long"
上面的程式碼利用 refine 關鍵字,重新定義 String 類別並且打包ㄧ個 length() 方法。就如同其他的 Refinements, Refinement Wrapper 也只能活到該檔案結束為止。
就因為 Refinements 所能影響的範圍是區域性,所以通常被視為比 Around Alias 更安全的選項。
還記得第八天所介紹的 Module#prepend() 方法嗎?Include() 與 prepend() 類似,都可以將方法引入 module,其中的差別在於 prepend 所引入的方法會在該類別的下方。
就因為這個區別,讓模組內的方法可以被覆寫並且呼叫 super 來取得未經修改的原始方法。
module ExplicitString
def length
super > 5 ? "long" : "short"
end
end
String.class_eval do
prepend ExplicitString
end
"War and Peace".length # => "long"
在以上程式碼,我們在 ExplicitString 模組內重新定義了 length() 方法,在以 class_eval() 方法打開 String 類別,並且以 prepend 該模組在 String 類別的下方。
最後的結果就是,之後任何物件呼叫 String#length() 都會得到修改後的回傳值。
目前為止我們介紹了三種 method wrappers: (1) Around Alias, (2) Refinement Wrapper, (3) Prepend Wrapper。 其中我比較偏好的是 Refinement Wrapper ,因為它提供了其他 Wrappers 都沒有的特性就是可用關鍵字 using 來決定覆寫方法可影響的範圍。然而,此特性的罩門就是容易搞會模組內呼叫方法的順序,因而得到與預想不同的回傳值。書中推薦的 method wrapper 則是 Prepend Wrapper,因為它的程式碼寫起來比較乾淨,也清楚許多。