開啟類
可以重新開啟已經存在的類並對之進行動態修改,即使像String或者Array這樣標準庫的類也不例外。這種行為方式稱之為開啟類(open class)
猴子補丁
如果你粗心地為某個類添加了新功能,同時覆蓋了類原來的功能,進而影響到其他部分的代碼,這樣的patch稱之為猴子補丁(Monkeypatch)
類與模組
Ruby的class關鍵字更像是一個範圍操作符,而不是型別宣告語句。class關鍵字的核心任務是把你帶到類的上下文中,讓你可以在裡面定義方法。
每個類都是一個模組,類就是帶有三個方法(new,allocate,superclass)的增強模組,通過這三個方法可以組織類的繼承結構,並建立對象
Ruby中的類和模組的概念十分接近,完全可以將二者相互替代,之所以同時保留二者的原因是為了保持代碼的清晰性,讓代碼意圖更加明確。使用原則:
- 希望把自己程式碼封裝含(include)到別的代碼中,應該使用模組
- 希望某段代碼被執行個體化或被繼承,應該使用類
- 模組機制可以用來實作類別似其它語言中的命名空間(Namespace)概念
Ruby中的::符號
Ruby中常量的路徑(範圍),類似與檔案系統中的目錄,通過::進行分割和訪問,預設直接以::開頭(例: :: Y)表示變數路徑的根位置
什麼是對象
對象就是一組執行個體變數外加一個指向其類的引用。對象的方法並不存在於對象本身,而是存在於對象的類中。
什麼是類
類就是一個對象(Class類的一個執行個體)外加一組執行個體方法和一個對其超類的引用。Class類是Module類的子類,因此一個類也是一個模組。
load與require方法的異同
通過load和require都可以進行匯入別人的代碼,不同的是load方法用來載入代碼,如果不希望汙染當前的命名空間,需要通過load(‘file.rb',true)顯式的要求建立一個匿名模組來,接管file.rb的常量,require用於匯入類庫,此外,就載入次數上load方法每次調用都會再次運行所負載檔案,require則對每個庫檔案只載入一次。
prepend、include與祖先鏈
祖先鏈用於描述Ruby對象的繼承關係,因為類與模組是父子關係,所以祖先鏈中也可以包含模組,prepend與include分別可以向鏈中添加模組,不同的是調用include方法,模組會被插入祖先鏈,當前類的正上方,而prepend同樣是插入到祖先鏈,但位置其他卻在當前類的正下方,另外通過Class.ancestors可以查看當前的祖先鏈
private規則
不能通過明確指定接受者來調用私人方法。私人方法只能通過隱性的接受者self調用(Object#send是個例外)
self相關
調用一個方法時,接受者會扮演self角色 任何沒有明確指定接受者的方法調用,都當做是調用self的方法 定義一個模組(或類)時,該模組(或類)會扮演self角色
對象、類與模組之間關係
上面Module.class指向的也是Class類,可以理解為上面方框內容均為Class,但他們的父子組織關係通過superclass建立並存在異同,可以通過Class.ancestors查看。
動態方法
動態調用方法
在Ruby中通過Object#send方法可以代替點標識調用對象的指定執行個體方法
範例程式碼
class MyClass def my_method(my_arg) my_arg * 2 endendobj = MyClass.newobj.my_method(3) #=> 6obj.send(:my_method, 3) #=> 6
上面代碼通過直接調用和使用send方法調用得到的結果是一樣的,使用send的好處是,可以在編碼中,動態決定方法調用。這個技巧在元編程中被稱為動態派發
另外需要指出的地方是通過Object#send不僅可以調用公用方法,也可以調用對象的私人方法。如果想保留對象的封裝特性,不向外暴露私人方法可以使用Object#public_send方法。
動態定義方法
除了方法的動態調用之外,Ruby還通過Module#define_method方法和代碼塊提供了動態方法定義方式
範例程式碼
class MyClass define_method :my_method do |my_arg| my_arg * 3 doendobj = MyClass.newobj.my_method(2) #=> 6
上面代碼通過define_method方法取代了關鍵詞def,其本質上都是相同的,只是在定義方式上,define_method的方式更加靈活一些,可以通過在編碼中通過推導,完成函數的定義,增加了實現的靈活性。
method_missing方法
嚴格意義上將method_missing方法,並不算是明確的定義(不會出現在methods列表中),其本質是通過方法尋找的機制來截獲調用資訊進而合理的給出相應方法的回應。有點類似與異常處理中的拋出異常,一層一層的往外拋。
method_missing利用的機制是,當一個對象進行某個方法調用的時候,會到其對應的類的執行個體方法中進行尋找,如果沒有找到,則順著祖先鏈向上尋找,直到找到BasicObject類為止。如果都沒有則會最終調用一個BasicObject#method_missing拋出NoMethodError異常。
當我們需要定義很多相似的方法時候,可以通過重寫method_missing方法,對相似的方法進行統一做出回應,這樣一來其行為就類似與調用定義過的方法一樣。
範例程式碼
class Roulette def method_missing(name, *args) person = name.to_s.capitalize super unless %w[Bob Frank Bill Honda Eric].include? person number = 0 3.times do number = rand(10) + 1 puts "#{number}..." end "#{person} got a #{number}" endendnumber_of = Roulette.newputs number_of.bobputs number_of.kitty
動態代理
對一些封裝過的對象,通過method_missing方法收集調用,並把這些調用轉寄到被封裝的對象,這一過程稱為動態代理,其中method_missing體現了動態,轉寄體現了代理
const_missing方法
與method_missing類似,還有關於常量的const_missing方法,當引用一個不存在的常量時,Ruby會把這個常量名作為一個符號傳遞給const_missing方法。
白板類(blank slates)
擁有極少方法的類稱為白板類,通過繼承BasicObject類,可以迅速的得到一個白板類。除了這種方法以外,還可以通過刪除方法來將一個普通類變為白板類。
刪除方法
刪除某個方法有兩種方式:
- Module#undef_method
- Module#remove_method
二者的區別是Module#undef_method會刪除所有(包括繼承而來的)方法。而Module#remove_method只刪除接受者自己的方法,而保留繼承來的方法。
動態方法與Method_missing的使用原則
當可以使用動態方法時候,盡量使用動態方法。除非必須使用method_missing方法(方法特別多的情況),否則盡量少使用它。