Ruby的面向?qū)ο蠓绞骄幊虒W(xué)習(xí)雜記
打開(kāi)類(lèi)
可以重新打開(kāi)已經(jīng)存在的類(lèi)并對(duì)之進(jìn)行動(dòng)態(tài)修改,即使像String或者Array這樣標(biāo)準(zhǔn)庫(kù)的類(lèi)也不例外。這種行為方式稱(chēng)之為打開(kāi)類(lèi)(open class)
猴子補(bǔ)丁
如果你粗心地為某個(gè)類(lèi)添加了新功能,同時(shí)覆蓋了類(lèi)原來(lái)的功能,進(jìn)而影響到其他部分的代碼,這樣的patch稱(chēng)之為猴子補(bǔ)丁(Monkeypatch)
類(lèi)與模塊
Ruby的class關(guān)鍵字更像是一個(gè)作用域操作符,而不是類(lèi)型聲明語(yǔ)句。class關(guān)鍵字的核心任務(wù)是把你帶到類(lèi)的上下文中,讓你可以在里面定義方法。
每個(gè)類(lèi)都是一個(gè)模塊,類(lèi)就是帶有三個(gè)方法(new,allocate,superclass)的增強(qiáng)模塊,通過(guò)這三個(gè)方法可以組織類(lèi)的繼承結(jié)構(gòu),并創(chuàng)建對(duì)象
Ruby中的類(lèi)和模塊的概念十分接近,完全可以將二者相互替代,之所以同時(shí)保留二者的原因是為了保持代碼的清晰性,讓代碼意圖更加明確。使用原則:
- 希望把自己代碼包含(include)到別的代碼中,應(yīng)該使用模塊
- 希望某段代碼被實(shí)例化或被繼承,應(yīng)該使用類(lèi)
- 模塊機(jī)制可以用來(lái)實(shí)現(xiàn)類(lèi)似其它語(yǔ)言中的命名空間(Namespace)概念
Ruby中的::符號(hào)
Ruby中常量的路徑(作用域),類(lèi)似與文件系統(tǒng)中的目錄,通過(guò)::進(jìn)行分割和訪(fǎng)問(wèn),默認(rèn)直接以::開(kāi)頭(例: :: Y)表示變量路徑的根位置
什么是對(duì)象
對(duì)象就是一組實(shí)例變量外加一個(gè)指向其類(lèi)的引用。對(duì)象的方法并不存在于對(duì)象本身,而是存在于對(duì)象的類(lèi)中。
什么是類(lèi)
類(lèi)就是一個(gè)對(duì)象(Class類(lèi)的一個(gè)實(shí)例)外加一組實(shí)例方法和一個(gè)對(duì)其超類(lèi)的引用。Class類(lèi)是Module類(lèi)的子類(lèi),因此一個(gè)類(lèi)也是一個(gè)模塊。
load與require方法的異同
通過(guò)load和require都可以進(jìn)行導(dǎo)入別人的代碼,不同的是load方法用來(lái)加載代碼,如果不希望污染當(dāng)前的命名空間,需要通過(guò)load(‘file.rb',true)顯式的要求創(chuàng)建一個(gè)匿名模塊來(lái),接管file.rb的常量,require用于導(dǎo)入類(lèi)庫(kù),此外,就加載次數(shù)上load方法每次調(diào)用都會(huì)再次運(yùn)行所加載文件,require則對(duì)每個(gè)庫(kù)文件只加載一次。
prepend、include與祖先鏈
祖先鏈用于描述Ruby對(duì)象的繼承關(guān)系,因?yàn)轭?lèi)與模塊是父子關(guān)系,所以祖先鏈中也可以包含模塊,prepend與include分別可以向鏈中添加模塊,不同的是調(diào)用include方法,模塊會(huì)被插入祖先鏈,當(dāng)前類(lèi)的正上方,而prepend同樣是插入到祖先鏈,但位置其他卻在當(dāng)前類(lèi)的正下方,另外通過(guò)Class.ancestors可以查看當(dāng)前的祖先鏈
private規(guī)則
不能通過(guò)明確指定接受者來(lái)調(diào)用私有方法。私有方法只能通過(guò)隱性的接受者self調(diào)用(Object#send是個(gè)例外)
self相關(guān)
調(diào)用一個(gè)方法時(shí),接受者會(huì)扮演self角色 任何沒(méi)有明確指定接受者的方法調(diào)用,都當(dāng)做是調(diào)用self的方法 定義一個(gè)模塊(或類(lèi))時(shí),該模塊(或類(lèi))會(huì)扮演self角色
對(duì)象、類(lèi)與模塊之間關(guān)系
上面Module.class指向的也是Class類(lèi),可以理解為上面方框內(nèi)容均為Class,但他們的父子組織關(guān)系通過(guò)superclass建立并存在異同,可以通過(guò)Class.ancestors查看。
動(dòng)態(tài)方法
動(dòng)態(tài)調(diào)用方法
在Ruby中通過(guò)Object#send方法可以代替點(diǎn)標(biāo)識(shí)調(diào)用對(duì)象的指定實(shí)例方法
示例代碼
class MyClass def my_method(my_arg) my_arg * 2 end end obj = MyClass.new obj.my_method(3) #=> 6 obj.send(:my_method, 3) #=> 6
上面代碼通過(guò)直接調(diào)用和使用send方法調(diào)用得到的結(jié)果是一樣的,使用send的好處是,可以在編碼中,動(dòng)態(tài)的決定方法調(diào)用。這個(gè)技巧在元編程中被稱(chēng)為動(dòng)態(tài)派發(fā)
另外需要指出的地方是通過(guò)Object#send不僅可以調(diào)用公共方法,也可以調(diào)用對(duì)象的私有方法。如果想保留對(duì)象的封裝特性,不向外暴露私有方法可以使用Object#public_send方法。
動(dòng)態(tài)定義方法
除了方法的動(dòng)態(tài)調(diào)用之外,Ruby還通過(guò)Module#define_method方法和代碼塊提供了動(dòng)態(tài)方法定義方式
示例代碼
class MyClass define_method :my_method do |my_arg| my_arg * 3 do end obj = MyClass.new obj.my_method(2) #=> 6
上面代碼通過(guò)define_method方法取代了關(guān)鍵詞def,其本質(zhì)上都是相同的,只是在定義方式上,define_method的方式更加靈活一些,可以通過(guò)在編碼中通過(guò)推導(dǎo),完成函數(shù)的定義,增加了實(shí)現(xiàn)的靈活性。
method_missing方法
嚴(yán)格意義上將method_missing方法,并不算是明確的定義(不會(huì)出現(xiàn)在methods列表中),其本質(zhì)是通過(guò)方法查找的機(jī)制來(lái)截獲調(diào)用信息進(jìn)而合理的給出相應(yīng)方法的回應(yīng)。有點(diǎn)類(lèi)似與異常處理中的拋出異常,一層一層的往外拋。
method_missing利用的機(jī)制是,當(dāng)一個(gè)對(duì)象進(jìn)行某個(gè)方法調(diào)用的時(shí)候,會(huì)到其對(duì)應(yīng)的類(lèi)的實(shí)例方法中進(jìn)行查找,如果沒(méi)有找到,則順著祖先鏈向上查找,直到找到BasicObject類(lèi)為止。如果都沒(méi)有則會(huì)最終調(diào)用一個(gè)BasicObject#method_missing拋出NoMethodError異常。
當(dāng)我們需要定義很多相似的方法時(shí)候,可以通過(guò)重寫(xiě)method_missing方法,對(duì)相似的方法進(jìn)行統(tǒng)一做出回應(yīng),這樣一來(lái)其行為就類(lèi)似與調(diào)用定義過(guò)的方法一樣。
示例代碼
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}" end end number_of = Roulette.new puts number_of.bob puts number_of.kitty
動(dòng)態(tài)代理
對(duì)一些封裝過(guò)的對(duì)象,通過(guò)method_missing方法收集調(diào)用,并把這些調(diào)用轉(zhuǎn)發(fā)到被封裝的對(duì)象,這一過(guò)程稱(chēng)為動(dòng)態(tài)代理,其中method_missing體現(xiàn)了動(dòng)態(tài),轉(zhuǎn)發(fā)體現(xiàn)了代理
const_missing方法
與method_missing類(lèi)似,還有關(guān)于常量的const_missing方法,當(dāng)引用一個(gè)不存在的常量時(shí),Ruby會(huì)把這個(gè)常量名作為一個(gè)符號(hào)傳遞給const_missing方法。
白板類(lèi)(blank slates)
擁有極少方法的類(lèi)稱(chēng)為白板類(lèi),通過(guò)繼承BasicObject類(lèi),可以迅速的得到一個(gè)白板類(lèi)。除了這種方法以外,還可以通過(guò)刪除方法來(lái)將一個(gè)普通類(lèi)變?yōu)榘装孱?lèi)。
刪除方法
刪除某個(gè)方法有兩種方式:
- Module#undef_method
- Module#remove_method
二者的區(qū)別是Module#undef_method會(huì)刪除所有(包括繼承而來(lái)的)方法。而Module#remove_method只刪除接受者自己的方法,而保留繼承來(lái)的方法。
動(dòng)態(tài)方法與Method_missing的使用原則
當(dāng)可以使用動(dòng)態(tài)方法時(shí)候,盡量使用動(dòng)態(tài)方法。除非必須使用method_missing方法(方法特別多的情況),否則盡量少使用它。
相關(guān)文章
Ruby使用設(shè)計(jì)模式中的代理模式與裝飾模式的代碼實(shí)例
這篇文章主要介紹了Ruby使用設(shè)計(jì)模式中的代理模式與裝飾模式的代碼實(shí)例,代理模式與裝飾模式都可以歸類(lèi)為結(jié)構(gòu)型的設(shè)計(jì)模式,需要的朋友可以參考下2016-03-03Ruby類(lèi)繼承、抽象類(lèi)、類(lèi)拓展混入、代理類(lèi)實(shí)例
這篇文章主要介紹了Ruby類(lèi)繼承、抽象類(lèi)、類(lèi)拓展混入、代理類(lèi)實(shí)例,本文對(duì)這幾種類(lèi)擴(kuò)展方法分別給出了代碼實(shí)例,需要的朋友可以參考下2015-01-01Ruby on Rails中Rack中間件的基礎(chǔ)學(xué)習(xí)教程
Rack是一個(gè)連接Ruby程序與服務(wù)器程序之間的中間件,甚至可以說(shuō)Rails也是在Rack的基礎(chǔ)上建立起來(lái)的,這里我們就來(lái)為大家?guī)?lái)Ruby on Rails中Rack中間件的基礎(chǔ)學(xué)習(xí)教程2016-06-06Ruby對(duì)比Python的優(yōu)勢(shì)和劣勢(shì)
這篇文章主要介紹了Ruby對(duì)比Python的優(yōu)勢(shì)和劣勢(shì),本文分別介紹了Ruby 比 Python 的優(yōu)勢(shì)和劣勢(shì),幫助你選擇哪種語(yǔ)言來(lái)學(xué)習(xí),需要的朋友可以參考下2015-05-05Ruby實(shí)現(xiàn)發(fā)送郵件的兩個(gè)方法
這篇文章主要介紹了Ruby實(shí)現(xiàn)發(fā)送郵件的兩個(gè)方法,本文講解了使用Net::SMTP、使用ruby gem:mail兩種方法實(shí)現(xiàn)郵件發(fā)送,需要的朋友可以參考下2015-01-01升級(jí)到mac 10.10之后使用pod出現(xiàn)問(wèn)題的解決方法
這篇文章主要介紹了升級(jí)到mac 10.10之后使用pod出現(xiàn)問(wèn)題的解決方法,需要的朋友可以參考下2014-07-07