Ruby學習之深入類

來源:互聯網
上載者:User

標籤:

在討論物件模型時,對類做了初步瞭解,關於類本身,還有許多知識需要學習。

類定義

Ruby中,可以用class關鍵字或者Class.new方法來定義一個類,在Ruby中,類定義的同時就是在運行代碼,類和方法、塊一樣,會返回最後一條語句的值,由於類也是一個對象(Class的執行個體),所以在類定義操作時,類本身就會充當self:

result = class MyClass
? ? puts self
? ? "return value"
end
puts result

以上語句輸出:

MyClass
return value

當前類

對象調用方法需要一個當前對象(self)作為接收者 ,如何來擷取當前對象呢?Ruby中,每當通過class開啟一個類時,這個類就會成為當前類。事實上,如果self是一個對象,那麼當前類就是這個對象的類。所以在頂級上下文中,self = main,main.class = Object,所以當前類就是Object。

上一篇隨筆中,我們知道可以用上下文探針Object#instance_eval()方法,該方法可以使調用的對象成為self,並且傳遞塊來訪問修改該對象。在類中,也有類似的方法:Module#class_eval(),class_eval()方法會使調用類成為當前類,並且修改添加類中的方法、屬性。這個方法可以在類名字未知時,修改類的方法和屬性。class關鍵字只能使用常量作為類名,而Module#class_eval則可以使用變數作為類名。

類執行個體變數和類變數

class MyClass
? ? @var = 1
? ? def self.read
? ? ? ? puts @var
? ? end
? ? def read
? ? ? ? puts @var
? ? end
? ? def write
? ? ? ? @var = 2
? ? end
end
obj = MyClass.new
obj.write
obj.read
MyClass.read

在上面的代碼中,執行obj.write時,此時self是obj對象,@var作為obj的一個執行個體變數,存在於obj中,執行MyClass.read時,@var作為MyClass的類執行個體變數(強調,類也是對象,MyClass.class = Class,所以類執行個體變數實際上是Class類對象的執行個體變數),存在於MyClass中,不能被子類和執行個體所訪問。

如果要在類中定義一個變數並且可以被子類繼承,則需要用到類變數,類變數是由@@開頭的變數:

class MyClass
? ? @@var = 1
end

如上定義一個類變數@@var,就可以被class MySubClass < MyClass類繼承該類變數。
類變數有一個問題,他並不屬於類本身,而是屬於類體繫結構:

@@var = 1
class MyClass
? ? @@var = 2
end
puts @@var?

上述代碼會有警告:warning: class variable access from toplevel?
由於@@var = 1執行時,self=main,main.class = Object,所以@@var屬於Object,同時也屬於Object的所有後代,包括MyClass,所以,在頂級上下文中使用類變數是比較危險的行為,同時,也應該減少類變數的使用,而用類執行個體變數來代替。

類方法

由於類也是一個對象,所以類也可以調用方法:

my_obj.my_method
MyClass.my_class_method

當一個對象調用方法時,其實是一個變數引用的對象調用了方法,而當一個類調用方法時,其實是一個常量引用的對象(Class 的執行個體) 調用了方法。

在之前學習動態調用時,使用到self.define_component()傳入一個參數作為方法名字並且動態定義該方法:

class Computer
? ? def self.define_component(name)
? ? ? ? define_method(name){
? ? ? ? puts "getting #{name} info"
? ? ? ? puts "getting #{name} price"
? ? }
? ? end
? ? define_component :mouse
?end

此時,self是Computer,所以define_component就是一個類方法,並且可以在Computer中直接調用define_component方法,這樣的方法稱為類宏。

類宏在很多的地方都有很好的應用,比如在類中添加一個屬性,則需要添加一個讀方法和一個寫方法對一個執行個體變數進行操作,這樣的方法定義在很多的類中都會重複操作,這時,使用Module#attr_writer()、Module#attr_read()、Module#attr_accessor()就可以

意識到對象的方法應該存放在類中,那麼類的方法應該存放於Class中,那樣的話,正如繼承自類的對象都會有類的執行個體方法,是否其他的類也會有這個類方法了呢?

class MyClass
? ? def self.my_class_method
? ? ? ? "This is a class_method"
? ? end
end
class MyClass2
end
puts MyClass.methods.grep(/my_class_method/)
puts MyClass2.methods.grep(/my_class_method/)
puts Class.instance_methods.grep(/my_class_method/)

事實上,上述代碼只會列印一行”This is a class_method“,也就是說,該類方法只存在於MyClass中,而不是Class的執行個體方法,更加不會被其他的類所繼承,那麼類方法到底存放在哪裡呢?

單件方法和單件類

在Ruby中,對象的類型是鴨子類型,也就是說,一個對象的類型並不是由他的類所決定,而是看它能響應哪些方法。對象只是繼承了類中的方法,同樣,他也可以有自己獨一無二的方法,這個方法就稱為單件方法:singleton_methods。由於這個方法是對象所專屬,並不存在於對象的類中,那麼這個單件方法是如何被調用的呢?

每個對象都有一個隱藏的類:單件類:EigenClass,當一個對象存在單件方法時,會先從對象的單件類中尋找,而對象單件類又繼承於該對象的類,所以如果在單件類中無法找到,則順其自然地從父類也就是對象的類中尋找,然後和一般的方法一樣從祖先鏈中尋找,直到找到或者找不到而調用missing_method。

類也是對象,所以也有單件類方法,所以類也有一個EigenClass,並且EigenClass的父類就是父類的EigenClass,所以類方法可以被子類所調用,但是卻不能被其他的類所訪問。

EigenClass也是一個類,所以EigenClass也有一個EigenClass。

Ruby中,可以使用以下方式訪問單件類:

class << MyClass
? ? def my_singleton_class
? ? end
end

同時,MyClass如果是obj就是給對象添加一個單件方法, class << 就表示需要訪問誰的單件類。

知道了這一點,就可以給類也添加屬性。一個對象的屬性就是對對象的執行個體變數實現讀寫操作,類的屬性就是對類執行個體對象實現讀寫操作,用類宏實現:

class MyClass
? ? attr_accessor :a
? ? class << self
? ? ? ? attr_accessor :b
? ? end
?end?

現在,MyClass有一個執行個體屬性a,並且有一個類屬性b,並且該屬性不會被其他類訪問而打亂整個命名空間,會被子類繼承。

通過模組可以大量新增一些打包好的類執行個體方法,那麼如何通過模組來添加類方法呢?

class MyClass
? ? include MyModule1
? ? class << self
? ? ? ? include MyModule2
? ? end
end

此時,MyClass中添加了MyModule1中的方法作為執行個體方法,並且添加了MyModule2中的方法作為類方法。這種應用相當普遍,所以有Object#extend()方法專門處理這些問題:

class MyClass
? ? extend MyModule2
end
obj = MyClass.new
obj.extend MyModule1

extend方法實際上在接收者的EigenClass中包含了模組的捷徑。

別名通過alias關鍵字可以給方法添加一個別名,alias :another_name :my_method,注意中間沒有逗號,因為alias是一個關鍵字而不是方法(關鍵字又是在結構的什麼位置中,和方法的區別又是什麼呢?)。調用新的名字時,會調用添加別名時的原來的方法。

對命名了別名後的方法進行重定義,那麼別名方法引用的還是原始方法,根據這個特性,可以實現環繞別名的技術:

class MyClass
? ? alias :real_length :length
? ? def length
? ? ? ? real_length > 5 ? ‘long’ : ’short’
? ? end
end

MyClass#length方法調用別名real_length時,其實調用的是原始的length方法,這種方式有點類似於組合語言中經常用到的保護現場, 在gem中用環繞別名實現版本控制。環繞別名是一個猴子補丁,可能會造成衝突的問題,在使用時尤其要注意。

Ruby學習之深入類

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.