# 1 單例方法的一種寫法和定義 # 在Ruby裏,能夠給具體的實例對象添加實例方法,這個方法只屬於這個實例 # 對象,咱們把這樣的方法稱之爲單例方法。 # 單例方法也叫做單件方法。定義單例方法,首先要生成一個實例對象,其次, # 要在方法名前加上對象名和一個點號「.」。 # 在下面示例中,對象p1不能夠laugh , laugh方法只屬於p2對象。 # 實例方法,屬於類的每一個實例對象。單例方法只出如今單個實例對象中。用單 # 例方法能夠極大地豐富多態性在Ruby中的表現力。 class Person end p1 = Person.new p2 = Person.new def p1.lagugh puts '這是p1所獨有的單例子方法' end # 2 接下來咱們再看下導入模塊 module MyModule def a_method puts 'Hello MyModule' end end # 3 直接extend導入 將模塊的實例方法變爲類方法 class F extend MyModule end F.a_method # 4 直接include導入 將模塊的實例方法變爲實例方法 class H include MyModule end h = H.new h.a_method # 5 class << self 中 include Mymodule # 此寫法與類方法相同 使用include導入後變成類方法 class G class << self include MyModule end end G.a_method # 6 用class << self 的寫法沒法使用extend # class H # class << self # extend MyModule # end # end # H.a_method # h.a_method # H.a_method # 7 使用實例 extend導入 obj = Object.new obj.extend(MyModule) obj.a_method # 8 先說結論,本質上混入 module 都是將 Module 中的方法引入到對象(obj, 類E, 類F)的singleton_class(單件類)中. # Ruby容許給單個對象增長方法,這種只針對單個對象生效的方法,稱爲單件方法。 # 9 extend與inclue 的區別 # include include to Object class # extend include to Obj's singleton class # 10總結 在類定義中使用include和extend時候 # include將類中的實例方法mixin變成類的實例方法 # extend將類中的實例方法變成類方法 # 但include可使用定義類方法的class << self方法導入 # extend則能夠經過實例mixn 究其緣由 # include是將模塊中的方法mixin對象的類中 # extend將模塊中的方法mixin對象的單例類中 # 11 單例方法只屬於這個對象 不屬於這個類 這個類的其餘實例都沒有這個方法 由於單例方法就是這個類的 單例類的實例方法 # 每一個對象的singleton class都不同 每一個對象都有本身的singleton class str = 'Hello Ruby' str2 = 'Hello Java' def str.foo puts 'str的單例方法' end str.foo # str2是沒有foo這個屬於str的單例方法的 # str2.foo # 12 經過輸出可知 str 和 str2 這兩個類的內存地址不一樣 是相同類型的不相等的類 # str 和 str2 的類型也是 Class 類 把 Class當作對象 p str2.singleton_class p str.singleton_class # singleton_class 也是 Class類的一個對象 p str.singleton_class.class # str2的實例方法中就沒有str實例方法foo # 13 因此 方法foo 能夠當作是這個單例類的實例對象的方法 p str.singleton_class.instance_methods(false) p str2.singleton_class.instance_methods(false) p str.methods.grep(/foo/) p str2.methods.grep(/foo/) # 14 Ruby中類也是對象,而類名只是常量,因此在類上調用方法其實跟在對象上調用方法同樣: # 15 類方法的實質是:它是一個類的單件方法,實際上若是比較單件方法的定義和類方法的定義,會發現其實兩者是同樣的. ## 在定義單件方法時,存在着類似的地方 ## 兩者均使用了def關鍵詞作定義。 ## 上面的object能夠是*對象的引用、常量類名或者self。 # 16 類方法定義與單例方法定義的比較 obj = Object.new # 17 單例方法的定義 def obj.a_singleton_method end # 18 類方法的定義 class Myclass def self.clazz_method1 puts '類方法1' end def Myclass.clazz_method2 puts '類方法2' end class << self def clazz_method3 puts '類方法3' end end end class << Myclass def clazz_method4 puts '類方法4' end end def Myclass.clazz_method5 puts '類方法5' end Myclass.clazz_method1 Myclass.clazz_method2 Myclass.clazz_method3 Myclass.clazz_method4 Myclass.clazz_method5 # 17 因此,單件方法能夠認爲是添加在對象的某個空間內只針對該對象有效的方法. # 若是對象是個實例對象,添加的方法就是他的單件方法, # 若是對象是個類,添加的方法就是這個類的類方法. # 18 咱們知道Ruby中對象的方法的查找順序是:先向右,再向上,其含義就是先向右找到對象的類, # 先在類的實例方法中嘗試查找,若是沒有找到,再繼續順着祖先鏈找. # 前面介紹的單件方法是指那些只針對某個對象有效的方法,那麼若是爲一個對象定義了單件方法, # 那麼這個單件方法的查找順序又應該是怎樣的? class Myclass def my_method end end obj = Myclass.new def obj.my_singleton_method end # 19 首先,單件方法不會在obj中,由於obj不是一個類,其次它也不在MyClass中, # 那樣的話全部的MyClass實例都應該能共享調用這個方法,也就構不成單件類了. # 同理,單件方法也不能在祖先鏈的某個位置(相似superclass: Object)中. # 正確的位置是在單件類中,這個類其實就是咱們在irb中向對象詢問它的類時(obj.class)獲得的那個類, # 不一樣的是這類與普通的類仍是有稍稍不一樣的.也能夠稱其爲元類或本徵類. # 前說起到的實例對象的單件方法和類的類方法在建立上是相似的, # 因此,經過的關鍵詞class配合特殊的語法應該能夠將其取到.class << obj. # 20 若是存在單件方法那麼查找順序先去類去中的單例方法 class C def a_method puts 'C#a_method' end end class D < C end d = D.new d.a_method # C#a_method puts 'abc'.singleton_class #<Class:#<String:0x0000000002ec90a8>> String類的實例 class << d def d_method puts 'D#d_method' end end puts d.singleton_class #<Class:#<D:0x0000000002ec9238>> Class類的實例 puts d.singleton_class.class # singleton_class 的類型是Class ## 既然d.singleton_class 也是個類,那麼久試着查一下他的父類 puts d.singleton_class.superclass # D ## singleton_class是一個類,那麼它確定有父類,它的父類就是對象所屬的類 ## 單例方法則存在於對象的特徵類中 # 單例方法存在於 對象單例類的實例方法中 puts d.singleton_class.instance_methods.grep(/d_method/) ## d_method puts d.singleton_class.instance_methods(false) # 打開單件類 # Ruby 中 class 做用是把代碼的上下文變換到這個類中,也就是『打開類』, # 同一個類能夠在任何地方被打開,也所以別人的類能夠被本身隨意打開並改寫. # Ruby提供了兩種方法獲取單件類的引用,一種是經過傳統的關鍵詞class配合特殊的語法。 # 經過關鍵字 class << obj 打開實例對象的單件類空間,爲對象添加一系列的單件方法. # 單件類的本質仍是一個類,是一個擁有惟一實例的類,因此叫單件類,或者單例類. # 定義對象的一系列單件方法 # class << an_object # def a_singleton_method # puts '單例方法1' # end # end obj = Object.new singleton_class = class << obj self end p singleton_class.class # 另外一個方法是,經過Object#singleton_class方法來得到單件類的引用: p singleton_class = 'abc'.singleton_class #<Class:#<String:0x0000000002ed7d10>> p singleton_class.class # => Class # 單件類的特性 # 1. 每一個單件類只有一個實例(被稱爲單件類的緣由),並且不能被繼承. # 2. 單件類是一個對象的單件方法的存活所在. # 3. 引入單件類後的方法查找 # 基於上面對單件類的基本認識,引入單件類後,Ruby的方法查找方式就不該該是先從其類(普通類)開始, # 而是應該先從對象的單件類中開始查找,若是在單件類中沒有找到想要的方法,它纔會開始沿着類(普通類)開始, # 再到祖先鏈上去找.這樣從單件類以後開始,一切又回到了咱們在沒有引入單件類時候的次序. # 類方法,實例方法,單例方法 # 類方法只有類自己能夠調用,在ruby中,類方法是一種特殊的單例方法. # 以前的例子中能夠獲得這樣的結論,singleton_class也是一種類, # 在ruby中全部的類又都是對象.對象都有對應的singleton_class. # Ruby 中方法的查找 遵循的規則是 'one step to the right, then up',即 向右一步而後向上. # Ruby中查找順序 # 1singleton method defined on object # 1* super class of singleton class # 2 instance method defined in class # 3 instance method defined in module # reverse order in which they were included # 4 instance method defined in ancestors # 5 method_missing defined in Kernel module class C def a_method puts 'C#a_method' end def self.a_class_method puts 'C#a_class_method' end end class D < C end obj = D.new class << obj def a_singleton_method puts 'obj#a_singleton_method' end end # 欲調用 obj.a_singleton_method # 此時調用對象是obj ,則會先去 #obj 處查找(向右一步) # #obj puts obj.a_singleton_method #obj#a_singleton_method # 欲調用 D.a_class_method # 此時調用對象是D ,則會先去 #D 出查找(向右一步) # D.singleton_class 而後往上查(superclass) # 單例類的父類就是對象所屬的類 puts D.singleton_class.superclass #<Class:C> puts D.singleton_class.class #Class puts D.singleton_class.class.superclass #Module # #D -> #C # D的類方法方法 a_class_method 在 #C 裏 puts D.a_class_method ## C#a_class_method # 欲調用 obj.a_method # 此時調用對象是obj ,則會先去 #obj 處查找(向右一步) puts obj.singleton_class ## #<Class:#<D:0x007f9e88340b60>> # obj.singleton_class 而後往上查(superclass) puts obj.singleton_class.superclass ## D # #obj 的父類 是 D 而後往上查(superclass) puts D.superclass ## C # D 的父類 是 C # 查找順序即: #obj -> D -> C # D的實例方法 a_method 在 類C 裏 obj.a_method ## C#a_method ###module module C def foo puts 'foo in C' end end module D def foo puts 'foo in D' end end ####classess class A def foo puts 'foo in A' end end # class B < A # # include C # # include D # # def foo # # puts 'foo in B' # # end # end class E end a = A.new # b = B.new e = E.new # def b.foo # puts 'singleton foo' # end # 1 第一種狀況有單例方法 # 輸出 foo in A 和 singleton foo a.foo # b.foo e.foo # 2 第二種狀況去除 單例方法 # 輸出 foo in A foo in B # 3 第三種狀況去除 類中的實例方法 # 輸出foo in A 和 foo in D # 4 第四種狀況 從父類中找實例方法 # 輸出foo in A 和 foo in A # 5 父類中找不到的話就是方法丟失 # 總結找方法 先找單例中的方法 而後是類中定義的實例方法 模塊中的實例方法 祖先類的實例方法 underfined method