Ruby之方法的妙用

[b]方法的查找與執行[/b]
Ruby中對象的方法都定義在類中,當對象想要執行一個方法時,首先需要找到該方法,而Ruby編譯器查找方法的方式就是,第一步在自己的類中找,沒有的話,就沿着該類的祖先鏈(ancestors)一直往上找。
String.ancestors        # => [String, Comparable, Object, Kernel, BasicObject]

爲什麼這兒會出現Comparable和Kernal, 這是因爲Module的機制,當一個類include一個模塊時,編譯會把該模塊放在最靠近該類的祖先鏈上, String類include了Comparable模塊,而Kernal則是被Object類include的。

方法執行的時候需要一個接收者,方法就會在接收者對象中被執行,該接收者就是所謂的self對象。一般情況下,self對象是由最後一個接收該方法的對象擔當,在類和模塊的定義中(並且在任何方法的定義外),self對象由類或模塊擔任。

[b]動態調用方法[/b]
通常方法的調用方式是“對象名.方法名”,在Ruby中有一個很酷的特性,可以通過send()方法,把想調用的方法名作爲參數,這樣就可以在代碼運行時,直到最後時刻才決定調用哪個方法,這種技術稱之爲動態派發(Dynamic Dispatch)。這個技術非常有用,比方說,當在項目中有一個配置文件對象,會根據配置文件初始化,在使用過程中,不同用戶可能會設置不同的值。通常做法是,判斷屬性的鍵值是對應到哪個屬性,然後,調用對應的set方法,代碼如下:
config.name = v if isNameProperty?(k) 
config.password = v if isPasswordProperty?(k)
config.port = v if isPortProperty?(k)
...

看着這麼一堆的長得像親兄弟似的代碼,不由得產生一種閹掉它們的衝動。如果使用動態調用方法的話,代碼可以簡化如下:
load_config('config.properties').each do |k, v| 
config.send("#{k}=", v)
end

根據獲取的每個鍵值,去調對應屬性的set方法,代碼清爽很多,而且以後擴展config對象不需要修改load方法。

[b]動態定義方法[/b]
除了動態調用方法外,Ruby甚至支持動態定義方法,通過使用Module#define_method()方法,提供一個方法名和一個充當方法體的塊即可定義一個方法。例:
class MyClass 
define_method :doubleString do |args|
args * 2
end
end
t = MyClass.new
puts t.doubleString("he") # => hehe

有了這個黑魔法之後,以後,就可以多個相似方法中不同的部分抽出來作爲參數,然後,使用一個方法定義搞定所有的方法。

[b]method_missing()方法[/b]
Ruby是動態語言,編譯器並不會檢測方法調用時的行爲,因此你可以調用一個不存在的方法。 在運行時,所有找不到對應方法的調用都會調用一個method_missing()方法,該方法定義在Kernal模塊中,因此每個對象都繼承了該方法。在kernal中method_missing()方法,會拋出一個NoMethodError的異常,這就是編譯器所做的工作。

但是,我們可以覆寫這個方法,讓它變得很有意思。比方說,創建一個Struct,當你想要新的屬性時,只需要給它賦個值就神奇的產生了。
class MyStruct 
def initialize
@attributes = {}
end

def method_missing(name, *args)
attribute = name.to_s
if attribute =~ /=$/
@attributes[attribute.chop] = args[0]
else
@attributes[attribute]
end
end
end
s = MyStruct.new
s.weibo = "@xianlinbox"
puts s.weibo # => @xianlinbox

這種,從調用者角度看,跟普通方法沒什麼區別,但是實際接收者卻並沒有相對應的方法的方法,Ruby術語稱之爲幽靈方法(Ghost Method)。對於幽靈方法,除了自定義該方法以外,還可以把該方法轉發給另外一個對象的方法,當然,你可以在轉發前後包裝一些自己的邏輯,這種處理技術稱之爲動態代理(Dynamic Proxy),和前面提到的動態調用異曲同工。

使用了method_missing處理幽靈方法後,所有不存在方法的調用都會到這裏來,這可能會導致一些錯誤信息不直觀的問題(你看不到NoSuchMethod這樣的提示了),因此, 在使用method_missing方案的時候,一定要限定其使用範圍,並且調用父類的super.method_missing方法,讓不屬於這個範圍的,該報什麼錯還是報什麼錯。

[b]白板類與保留方法[/b]
每一個類都有從父類繼承下來的一堆方法,在使用動態代理技術時,如果一個幽靈方法和真實的方法(你沒有意料到的繼承來的方法)發生名字衝突時,後者會獲勝,從而導致系統報錯。因此在使用動態代理技術時, 可以在代理類中刪除絕大多數繼承來的方法,避免發生名字衝突,刪除了繼承方法的類,就是所謂的白板類(Blank Slate),刪除類方法的途徑有2個,一個是調用Module#undef_method方法,一個是調用Module#remove_method方法。

在Ruby的Object類中,有一些方法是內部使用的,如果對其重新定義,或刪除,可能導致Ruby莫名其妙的掛掉,爲了防止這種事情的發生,Ruby在這些方法名前面用“__”打頭,這些方法稱之爲保留方法,當你試圖修改這些方法時,Ruby會給出警告。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章