singleton_class 和Singleton Method
class 混入 module 时的相关思考
在Ruby里, 咱们能够经过在 class 里混入 module 来达到一些用途,那么 module 里面的代码是怎么混入class 里,又是被放置在了什么地方呢?ruby
module MyModule def a_method puts "hello, MyModule#a_method" end end ## 直接extend module class F extend MyModule end F.a_method ## hello, MyModule#a_method ## class << self 中 include MyModule class E class << self include MyModule end end E.a_method ## hello, MyModule#a_method ## 对象也是能够混入一个module obj = Object.new obj.extend(MyModule) obj.a_method ## hello, MyModule#a_method
上面的例子包含了三种引入 module 的方法:spa
实例对象(obj) extend MyModule,code
类E 的class << self 中 include MyModule对象
类F extend MyModule.blog
从这个咱们能够明白,Ruby能够为对象混入一个 module 来做为拓展,那么混入的代码被放置到哪里了呢?继承
先说结论,本质上混入 module 都是将 Module 中的方法引入到对象(obj, 类E, 类F)的singleton_class(单件类)中.
在讲单件类以前,先来看看单件方法.
Singleton Method (单件方法)
在 Ruby 里string
Ruby容许给单个对象增长方法,这种只针对单个对象生效的方法,称为单件方法。例如:
str = "I am a string" def str.title? self.upcase == self end str.title? # => false str.methods.grep(/title?/) # => [:title?] str.singleton_methods #=> [:title?] str.class # => String String.new.title? #=> NoMethodError ## 除此以外,还能够用 define_singleton_method 来定义单例方法 guy = "Bob" guy.define_singleton_method(:hello) { "#{self}: Hello there!" } guy.hello #=> "Bob: Hello there!"
Ruby中类也是对象,而类名只是常量,因此在类上调用方法其实跟在对象上调用方法同样:
类方法的实质是:它是一个类的单件方法,实际上若是比较单件方法的定义和类方法的定义,会发现其实两者是同样的.it
## 在定义单件方法时,存在着类似的地方 ## 两者均使用了def关键词作定义。 ## 上面的object能够是*对象的引用、常量类名或者self。 def obj.a_singleton_method end def MyClass.another_class_method end
因此,单件方法能够认为是添加在对象的某个空间内只针对该对象有效的方法.
若是对象是个实例对象,添加的方法就是他的单件方法,
若是对象是个类,添加的方法就是这个类的类方法.
singleton_class (单件类)
咱们知道Ruby中对象的方法的查找顺序是:先向右,再向上,其含义就是先向右找到对象的类,先在类的实例方法中尝试查找,若是没有找到,再继续顺着祖先链找.
前面介绍的单件方法是指那些只针对某个对象有效的方法,那么若是为一个对象定义了单件方法,那么这个单件方法的查找顺序又应该是怎样的?class
class MyClass def my_method end end obj = MyClass.new def obj.my_singleton_method end
首先,单件方法不会在obj中,由于obj不是一个类,其次它也不在MyClass中,那样的话全部的MyClass实例都应该能共享调用这个方法,也就构不成单件类了.同理,单件方法也不能在祖先链的某个位置(相似superclass: Object)中.
正确的位置是在单件类中,这个类其实就是咱们在irb中向对象询问它的类时(obj.class)获得的那个类,不一样的是这类与普通的类仍是有稍稍不一样的.也能够称其为元类或本征类.
前说起到的实例对象的单件方法和类的类方法在建立上是相似的,因此,经过的关键词class配合特殊的语法应该能够将其取到.class << obj.module
这是实例方法的查找规则
这是单件方法的查找规则
class C def a_method puts "C#a_method" end end class D < C end d = D.new d.a_method puts "abc".singleton_class class << d def d_method puts 'D#d_method' end end puts d.singleton_class ## #<Class:#<D:0x007f9e88392d70>> puts d.singleton_class.class ## Class ## 既然d.singleton_class 也是个类,那么久试着查一下他的父类 puts d.singleton_class.superclass ## D ## singleton_class是一个类,那么它确定有父类,它的父类就是对象所属的类 ## 单例方法则存在于对象的特征类中 puts d.singleton_class.instance_methods.grep(/d_method/) ## d_method
打开单件类
Ruby 中 class 做用是把代码的上下文变换到这个类中,也就是『打开类』,同一个类能够在任何地方被打开,也所以别人的类能够被本身随意打开并改写.
Ruby提供了两种方法获取单件类的引用,一种是经过传统的关键词class配合特殊的语法。
经过关键字 class << obj 打开实例对象的单件类空间,为对象添加一系列的单件方法.单件类的本质仍是一个类,是一个拥有惟一实例的类,因此叫单件类,或者单例类.
class << an_object # 本身的代码,定义对象的一系列单件方法 end obj = Object.new singleton_class = class << obj self end singleton_class.class # => Class
另外一个方法是,经过Object#singleton_class方法来得到单件类的引用:
singleton_class = 'abc'.singleton_class # => #<Class: #<String:0xxxxxx>> singleton_class.class # => Class
单件类的特性
1. 每一个单件类只有一个实例(被称为单件类的缘由),并且不能被继承.
2. 单件类是一个对象的单件方法的存活所在.
3. 引入单件类后的方法查找
基于上面对单件类的基本认识,引入单件类后,Ruby的方法查找方式就不该该是先从其类(普通类)开始,而是应该先从对象的单件类中开始查找,若是在单件类中没有找到想要的方法,它才会开始沿着类(普通类)开始,再到祖先链上去找.这样从单件类以后开始,一切又回到了咱们在没有引入单件类时候的次序.
类方法,实例方法,单例方法
类方法只有类自己能够调用,在ruby中,类方法是一种特殊的单例方法.以前的例子中能够获得这样的结论,singleton_class也是一种类,在ruby中全部的类又都是对象.对象都有对应的singleton_class.
Ruby 中方法的查找规则
注释: 以#号标示的是singleton_class, c标示 (eigen)class, s表示 superclass
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 ## #C # #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
Ruby 中方法的查找 遵循的规则是 'one step to the right, then up',即 向右一步而后向上.