ruby面向?qū)ο蟪醪嚼斫?/h3>
類與對(duì)象無疑是ruby的核心楚堤,可能最開始看上去有一點(diǎn)困惑疫蔓,似乎有太多的概念:類、對(duì)象身冬、類對(duì)象衅胀、模塊、實(shí)例方法酥筝、類方法滚躯、單例類.... 太多太多,許多從事ruby開發(fā)的工程師們嘿歌,大多都有其他編程語(yǔ)言的開發(fā)經(jīng)驗(yàn)掸掏,也會(huì)借助其他語(yǔ)言(比如java)的面向?qū)ο罄砟顏砝斫鈘uby,大多數(shù)情況下宙帝,能認(rèn)識(shí)到的也只是千篇一律的 class丧凤、new、initialize等等步脓,很經(jīng)典的Ruby元編程一書中愿待,也有不少朋友覺得面向?qū)ο筮@部分的概念比較繞腦,我決定記錄并分析一下
ruby虛擬機(jī)啟動(dòng)之初靴患,提供了哪些類, 模塊或者說對(duì)象
# 我們隨意創(chuàng)建一個(gè)類
class Aoo
def hello
puts 'hello world'
end
end
# 打印祖先鏈
puts Aoo.ancestors
>> [Aoo, Object, Kernel, BasicObject]
# 打印Aoo單件的祖先鏈
puts Aoo.singleton_class.ancestors
>> [#<Class:Aoo>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
# 打印祖先鏈中各個(gè)對(duì)象的類型
puts Class.class
>> Class
puts Module.class
>> Class
puts Object.class
>> Class
puts Kernel.class
>> Module
puts BasicObject.class
>> Class
puts Aoo.class
>> Class
類與對(duì)象無疑是ruby的核心楚堤,可能最開始看上去有一點(diǎn)困惑疫蔓,似乎有太多的概念:類、對(duì)象身冬、類對(duì)象衅胀、模塊、實(shí)例方法酥筝、類方法滚躯、單例類.... 太多太多,許多從事ruby開發(fā)的工程師們嘿歌,大多都有其他編程語(yǔ)言的開發(fā)經(jīng)驗(yàn)掸掏,也會(huì)借助其他語(yǔ)言(比如java)的面向?qū)ο罄砟顏砝斫鈘uby,大多數(shù)情況下宙帝,能認(rèn)識(shí)到的也只是千篇一律的 class丧凤、new、initialize等等步脓,很經(jīng)典的Ruby元編程一書中愿待,也有不少朋友覺得面向?qū)ο筮@部分的概念比較繞腦,我決定記錄并分析一下
# 我們隨意創(chuàng)建一個(gè)類
class Aoo
def hello
puts 'hello world'
end
end
# 打印祖先鏈
puts Aoo.ancestors
>> [Aoo, Object, Kernel, BasicObject]
# 打印Aoo單件的祖先鏈
puts Aoo.singleton_class.ancestors
>> [#<Class:Aoo>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
# 打印祖先鏈中各個(gè)對(duì)象的類型
puts Class.class
>> Class
puts Module.class
>> Class
puts Object.class
>> Class
puts Kernel.class
>> Module
puts BasicObject.class
>> Class
puts Aoo.class
>> Class
結(jié)論:
- Class, Module, Object, Kernel, BasicObject 這幾個(gè)對(duì)象早已被ruby虛擬機(jī)提供仍侥,并且ruby中所有對(duì)象都直接或者間接繼承了他們,眾所周知蚁廓,模塊是需要通過include引入的访圃,并非顯示繼承,不過從模塊被插入祖先鏈來看相嵌,也是一種變相的繼承腿时,并且模塊的此種特性,可以滿足多繼承需要
疑問:
- 為何Module.class得到的結(jié)果是Class饭宾,而Kernel.class得到的結(jié)果是Module
- Aoo的祖先鏈和Aoo單件的祖先鏈為何差距這么大
- 我們所謂的類批糟,比如Aoo, Object, BasicObject,或者我們所謂的模塊看铆,比如Module, Kernel徽鼎,它們的類型要么是Class,要么是Moudle弹惦,是不是意味著這些類都是對(duì)象否淤?
- 祖先鏈中,Class和Module居然在Object的下方棠隐,是不是意味著它們繼承了Object?
探究原生類或者對(duì)象的繼承關(guān)系
在面向?qū)ο蟮睦碚撝惺眨诵募匆磺薪詫?duì)象,ruby對(duì)此作出了很好的詮釋助泽,在ruby中啰扛,不管是+ - * / 還是一個(gè)我們所說的類,都是一個(gè)對(duì)象
雖然從祖先鏈來看嗡贺,頂層是BasicObject隐解,但實(shí)際上Class、Module才是ruby最初始的核心
我們剛才用.class方法獲取對(duì)象的類型诫睬,在ruby中煞茫,只有類型是Class的才可以被繼承,模塊只能混入摄凡,不可以繼承续徽,ruby里模塊的.class都為Module,但是Module本身是一個(gè)Class架谎,這是ruby自身的設(shè)定
我們把類型為Class的對(duì)象炸宵,叫做類對(duì)象,也就是.class是Class的對(duì)象谷扣,更多時(shí)候土全,我們直接稱作類,由于Class的類型也是Class会涎,所以Class是一個(gè)可以創(chuàng)建類對(duì)象的類對(duì)象
我們查看Class中的自有方法
# 查詢方法時(shí)帶上false參數(shù) 表示只查詢自有方法裹匙,而不查詢繼承得來的方法
puts Class.methods false
>> [] # 結(jié)論 -- Class類并沒有定義自己的類方法
puts Class.instance_methods false
>> [:allocate, :new, :superclass] # 結(jié)論 -- Class僅有3個(gè)公有實(shí)例方法,其中有創(chuàng)建對(duì)象必備的new方法
puts Class.private_methods false
>> [:inherited, :initialize]
puts Class.private_instance_methods false
>> [:inherited, :initialize]
# 結(jié)論 -- Class的私有方法中末秃,類方法和實(shí)例方法相同概页,是用于對(duì)象初始化的initialize
結(jié)論:
- Class中定義的方法很少,但是包含了面向?qū)ο蟮奶匦?/li>
- 我們知道练慕,Object/BasicObject的類方法中惰匙,已具備new方法(不知道的朋友可以自己打印出來看
- new方法定義在Class中技掏,只能從Class獲取,恰好Class中的new方法是實(shí)例方法(類的實(shí)例方法會(huì)作為對(duì)象的類方法)
- 并且BasicObject/Object的class屬性指明了類型是Class
-
證明
:Object/BasicObject..等等帶有new方法的類都是Class的對(duì)象项鬼,所以類也是對(duì)象的一種 -
注意
:Class的私有方法中哑梳,不論類方法還是實(shí)例方法都帶有構(gòu)造方法initialize,證明Class本身也是有實(shí)例化過程的(由虛擬機(jī)實(shí)例化)绘盟,Class本身也是一個(gè)對(duì)象鸠真,正因?yàn)槿绱耍珻lass可以調(diào)用實(shí)例方法的new去創(chuàng)建Object - Class非常特殊龄毡,它的類型就是自身吠卷,即Class.class === Class,這是ruby自身設(shè)定沦零,用于形成類型引用的閉環(huán)(下面討論)
以上根據(jù)Class的內(nèi)部方法定義祭隔,以及所有類都具有new方法,證明了所有類都是Class的對(duì)象蠢终,并且Class本身也是對(duì)象
我們?cè)倏纯碝odule
puts Module.methods false
>> [:nesting, :constants]
puts Module.instance_methods false
>> [:freeze, :===, :==, :<=>, :<, :<=, :>, :>=, :to_s, :inspect, :included_modules, :include?, :name, :ancestors, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :const_missing, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :public_constant, :private_constant, :singleton_class?, :include, :prepend, :module_exec, :class_exec, :module_eval, :class_eval, :method_defined?, :public_method_defined?, :private_method_defined?, :protected_method_defined?, :public_class_method, :private_class_method, :autoload, :autoload?, :instance_method, :public_instance_method]
puts Module.private_methods false
>> [:inherited, :initialize]
puts Module.private_instance_methods false
>> [:included, :extended, :prepended, :method_added, :method_removed, :method_undefined, :initialize_copy, :attr, :attr_reader, :attr_writer, :attr_accessor, :initialize, :initialize_clone, :remove_const, :append_features, :extend_object, :prepend_features, :refine, :using, :remove_method, :undef_method, :alias_method, :public, :protected, :private, :module_function, :define_method]
結(jié)論:
- Module中序攘,私有類方法帶有構(gòu)造方法initialize,證明Module本身也是有實(shí)例化過程的(由虛擬機(jī)實(shí)例化)寻拂,Module本身也是一個(gè)對(duì)象
- Module中定義了大量在面向?qū)ο笾谐S貌僮鞒痰欤梢钥醋鍪敲嫦驅(qū)ο蟮闹饕獙?shí)現(xiàn)
- 我們知道,Class雖然自己只有幾個(gè)方法祭钉,但是繼承而來的方法卻很多瞄沙,如果你打印出來看的話,和Module中提供的方法基本一致慌核,所以:Class繼承了Module
以上總結(jié)距境,Class和Module都是由虛擬機(jī)實(shí)例化的對(duì)象,既是對(duì)象也是類垮卓,他們是虛擬機(jī)初始階段提供的核心
在虛擬機(jī)已提供Class和Module的前提下垫桂,就可以開始正常的對(duì)象創(chuàng)建和繼承了,ruby以此為基礎(chǔ)粟按,又創(chuàng)建了Kernel模塊诬滩,BasicObject, Object
- Kernel -- 定義了大量常用工具方法比如輸入輸出之類的,通過Module.new創(chuàng)建
- BasicObject -- 基本是個(gè)空的類灭将,可以作為潔凈室疼鸟,通過Class.new創(chuàng)建
- Object -- 該類作為我們自定義類的基類,暴露給我們使用庙曙, 通過Class.new創(chuàng)建
疑問:
- Module哪來new方法空镜,雖然它和Class一樣,有虛擬機(jī)幫助實(shí)例化,既是類也是對(duì)象吴攒,但是Module不管類方法還是實(shí)例方法都沒有new张抄,它得從Class中繼承到new方法才行,然而剛才已經(jīng)證明Class繼承了Module
Ruby核心對(duì)象的繼承方式舶斧,其實(shí)是一個(gè)環(huán)
Module要想獲取new欣鳖,沒法繼承Class察皇,因?yàn)镃lass已經(jīng)繼承了Module茴厉,由于他們的.class都是Class,所以可以繼承Class的實(shí)例化對(duì)象Object(也可以叫做類對(duì)象)什荣,所以Class Module Object構(gòu)成了環(huán)狀繼承關(guān)系矾缓,Module和Class中的實(shí)例方法,在Object中以類方法的形式存在(因?yàn)镺bject是Class對(duì)象)稻爬,當(dāng)Module繼承Object之后嗜闻,獲取了Object的類方法,換而言之桅锄,此種方式琉雳,讓自己的實(shí)例方法轉(zhuǎn)變?yōu)榱祟惙椒ǎ@3個(gè)對(duì)象間形成了方法的大范圍共享友瘤,以此為基礎(chǔ)翠肘,才有了ruby各種面向?qū)ο髾C(jī)制
ruby的單件
我們回到之前提到的單件類的祖先鏈問題,對(duì)比下兩種情況下的祖先鏈
# 打印Aoo單件的祖先鏈
puts Aoo.singleton_class.ancestors
>> [#<Class:Aoo>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
# 為何Aoo的單件祖先鏈不是如下 辫秧?束倍??盟戏?绪妹?
[#<Class:#Aoo>, Class, Module, Object, Kernel, BasicObject]
# 打印Aoo實(shí)例化對(duì)象的祖先鏈
puts Aoo.new.singleton_class.ancestors
>> [#<Class:#<Aoo:0x007fdac100dba8>>, Aoo, Object, Kernel, BasicObject]
# 如果我們定義一個(gè)類去繼承Aoo
class Boo < Aoo
end
puts Boo.new.singleton_class.ancestors
>> [#<Class:#<Boo:0x007fdabf8d48d8>>, Boo, Aoo, Object, Kernel, BasicObject]
總結(jié):
- 如果你平時(shí)仔細(xì)觀察,會(huì)發(fā)現(xiàn)柿究,Class的直接對(duì)象(類對(duì)象)和普通類的對(duì)象邮旷,他們單間類的祖先鏈會(huì)出現(xiàn)這兩種情況
- 我們知道,ruby的單件是無限的蝇摸,除了單件婶肩,還有單件的單件 ......,可以無限遞歸下去探入,所以單件類在被我們使用前狡孔,是不會(huì)自己實(shí)例化的,只有當(dāng)我們?cè)L問單件的時(shí)候蜂嗽,才會(huì)實(shí)例化單件苗膝,并且這個(gè)單件也是當(dāng)前對(duì)象.class所指明類型的實(shí)例
當(dāng)你訪問對(duì)象單件的時(shí)候,絕對(duì)不僅僅只是根據(jù)類型再創(chuàng)建一個(gè)實(shí)例植旧,比如我們?cè)L問Aoo的單件辱揭,不僅生成了Class:Aoo离唐,還有Class:Object,Class:BasicObject
- 主要區(qū)別還是在于對(duì)象的.class是否是Class问窃,也就是看它是否是一個(gè)類對(duì)象亥鬓,如果當(dāng)前對(duì)象是一個(gè)類對(duì)象,證明它的單件也一定是一個(gè)Class的實(shí)例域庇,既然都是類對(duì)象嵌戈,就會(huì)有相同的繼承關(guān)系,所以Aoo單件的祖先鏈听皿,會(huì)先列出[#<Class:Aoo>, #<Class:Object>, #<Class:BasicObject>]
- 普通對(duì)象的的.class并非Class熟呛,對(duì)象自身并沒有所謂的繼承關(guān)系,所以普通對(duì)象的單件不涉及祖先鏈尉姨,直接就追溯到了類對(duì)象本身
最后
- Class和Module是由虛擬機(jī)提供的特殊類庵朝,之后的一切類都由Class創(chuàng)建
- 被new創(chuàng)建出來的,可以是對(duì)象又厉,也可以是類對(duì)象(類)九府,對(duì)象的.class屬性為Class的都是類對(duì)象,Class不僅可以創(chuàng)建類對(duì)象覆致,自身也是一個(gè)類對(duì)象
- Class中定義了創(chuàng)建對(duì)象的基本方法侄旬,比如new
- Module中定義了大量類操作相關(guān)方法
- Class、Module篷朵、Object勾怒、Kernel、BasicObjec這一層的初始化操作對(duì)用戶透明声旺,將Object作為自定義類的基類
- 類對(duì)象(類)的實(shí)例方法會(huì)作為對(duì)象的類方法
- Class笔链、Module、Object的繼承關(guān)系腮猖,讓他們的類方法與實(shí)例方法相同鉴扫,BasicObject只是沒有混入Kernel,作為一個(gè)類對(duì)象澈缺,Class和Module中的方法是必不可少的坪创,并不是所謂的內(nèi)部幾乎沒有方法
- 單件是訪問的時(shí)候產(chǎn)生,并且根據(jù)繼承關(guān)系不同姐赡,產(chǎn)生的單件個(gè)數(shù)不同莱预,會(huì)為單件生成一個(gè)完備的祖先鏈
ruby的元編程和面向?qū)ο蟮乃季S確實(shí)很繞,自己琢磨的過程中也只是略有收獲而已项滑!