理解对象模型

卡米洛·雷耶斯
分享

结构和层次结构背景

你有没有想过为什么Ruby会这样?

类A定义自我。my_method'hello' end def my_other_method 'hello again' end end A.my_method # => "hello" a = A.new a.my_other_method # => "hello again" a.my_method # => NoMethodError: undefined method

如果您来自Java等经典语言,则可以考虑这一点my_method很像一个静态方法。但是,有一些关键的区别:

  • 类方法只能从类中调用并继承。
  • 实例方法只能通过类的实例调用并继承。
  • 实例对象也可以声明“类”方法。

困惑了吗?

在本文中,我们将探索对象模型。这将使您更深入地了解Ruby堆栈和管理对象的方式。出于本文的目的,我们将使用Ruby MRI 2.1。如果有兴趣,我建议你动手irb把你的脚弄湿了。别忘了穿救生衣,因为我们要去游泳池的最深处。我也戴上我的。

什么是单例方法?

如果我告诉你Ruby中的每个方法都是单例方法呢?如果我说的是真的,那么每个单例方法都必须属于一个“类”单例对象。现在,如果您已经知道了经典语言中的单例设计模式,那么请忘记您所学的所有内容。如果您从未听说过单例方法,那么您已经领先一步了。让我们一起来看看:

类D;def method_one;'hello from method_one';结束;结束def .method_two;'hello from method_two';d.method_one # => "hello from method_one" d = d. new d.method_one # => "hello from method_one"

从Ruby的角度来看,这两个方法调用是相似的。理论上,我们将调用单例对象的单例方法。所以,当接收者Dd调用一个方法时,在Ruby中必须有一个查找,该查找一步直接到“类”,然后向上到祖先链。但是等等,你会说,一个不是实例方法,一个不是类方法吗?

让我们仔细看看这个兔子洞有多深:

D .class.instance_methods.grep(/method_one/) # => [:method_one]

到目前为止,一切顺利。接收器中的方法查找d一步右转到“类”并找到method_one。请记住,在Ruby中,类也是单例对象。但是D呢?

D.class.instance_methods.grep(/method_two/) # => []

等一下,有点不对劲。中没有定义我的方法所以这件事肯定比我们看到的要复杂。

好吧,有些事情感觉不太对,那么这样如何:

def d.method_three;'hello from method_three';结束;D .method_three # => "hello from method_three" D .class # => D .class.instance_methods.grep(/method_three/) # => []

等一下!method_three在类D中也没有定义。也许有什么关键的东西我们没有发现。有一件事是肯定的,对于这最后两次调用,它们都从假定的类中丢失了。为了让我们的单例方法假设有意义,这些方法必须属于一个“类”。让我们看看能否找到这些难以捉摸的类。

进入Eigenclasses

在Ruby中,术语似乎在如何调用这些元类上存在分歧。松本幸博“Matz”还没有宣布正式名字。但他似乎喜欢这个数学家喜欢的名字eigenclass.另一个公认的术语是单例类

这个词特征在德语中(发音:AYE-gun),大致意思是“自己的”。在本例中,是指对象的“自己的类”。在本文中,我们将继续使用本征类作为术语。

要在Ruby中找到这些难以捉摸的ufo,你可以做:

D .singleton_class # => #>

或者,如果你想用你疯狂的Ruby技能给你的朋友留下深刻的印象,那就hack起来吧:

类对象;def eigenclass;类<< self;自我;结束;结束;end D .eigenclass # => #>

有了这个代码,我们将重新开放对象,这是在祖先链(后面详细介绍)和添加一个方法。这个方法直接进入特征类并返回“self”,它是接收者的特征类。巧妙的把戏,是吧?

现在我们已经找到了特征类,让我们检查一下我的理论是否有意义:

D.eigenclass.instance_methods.grep(/method_two/) # => [:method_two] D.eigenclass.instance_methods.grep(/ method_three/) # => [:method_three]

大作。我们已经找到了自己的方法。注意,此方法查找与on查找没有任何不同method_one.回顾一下,这是直接进入“类”并沿着祖先链向上走的一步。Ruby似乎为我们将这些代码包装在一个名为singleton_methods,但现在我们知道这都是烟雾和镜子。

我应该提一下duck-typing,因为有些人害怕Ruby中的单例方法。鸭子打字是:“如果它走路像鸭子,叫起来像鸭子,那么它一定是鸭子。”在Ruby中,对象的“类型”是它所响应的一组单例方法。它是绑定到类定义。

我知道,你可能会在脑海中听到蜘蛛侠的本叔叔说:“能力越大,责任越大。”Ruby总是假定您负责使用这种强大的技术。

正在执行的方法查找

有了对Ruby的确切了解,我们应该能够划着桨向深水区前进。让我们起草一个快速的“实验老鼠”程序,这样我们就可以验证对象模型的工作:

C类;def an_instance_method;结束;end def C.a_class_method;结束类E < C;end obj = E.new def obj.a_singleton_method;结束

现在,让我们直接进入“类”,沿着祖先链往上走,看看一切是如何安排的:

Arr = [:an_instance_method,:a_class_method,:a_singleton_method] obj。eigenclass# => #> obj.eigenclass.instance_methods.select { |m| arr.include?(m) } # => [:a_singleton_method, :an_instance_method] obj.eigenclass.superclass # => E obj.class # => E E.superclass # => C C.instance_methods.select { |m| arr.include?(m) } # => [:an_instance_method] C.superclass # => Object Object.superclass # => BasicObject BasicObject.superclass # => nil

需要注意的重要一点是对象的本征类的超类是对象的类。我建议一遍又一遍地重复最后一句话,直到一切都了然于心。祖先链停留在类上,这就是为什么我们可以在obj的本征类中找到方法。如图所示,: an_instance_method属于C.同时,instance_methods也从祖先链中提取方法。

那么我们类的特征类呢?

E.eigenclass.superclass # => #<类:C> C.eigenclass.instance_methods. E.eigenclass # => #<类:E> E.eigenclass.superclass # => #<类:C>select {|m| arr.include?(m)} # => [:a_class_method] C.eigenclass.superclass # => # Object.eigenclass.superclass # => Class: > BasicObject.eigenclass.superclass # => Class。superclass # => Module模块>对象

看上面的例子,类的本征类的超类就是类的超类的本征类。现在,重复最后一个句子,超快!类和特征类的祖先链合并在对象.看吧,毕竟和“大男孩”一起游泳也没那么难。

但是,模块呢?

事实证明,在Ruby中,模块与类并没有太大的不同。关键的区别在于,当你输入超类时,它们不会出现在祖先链中。让我们看看是否有一种跟踪模块的方法,尝试:

模块F;def a_module_method;结束;E类结束;包括F;end E.superclass # => C .祖宗# => [E, F, C,对象,内核,BasicObject]

如您所见,当您添加一个模块时,它会被追加到祖先链。我们可以验证这一点:

E.instance_methods.include?(:a_module_method) # => true

总之

好了,现在是突击测验时间!

你能告诉我这样做会发生什么吗?

模块F;类克;结束;结束

在祖先链中是怎样的呢?有办法实例化类G吗?如果你完全迷路了,请留下评论。

Ruby对象模型是一种神奇而简单的模型。一旦你理解了它,你就会觉得整个语言都是正确的。我希望你现在对掌握这门美丽的语言有了更多的了解。

黑客快乐!

Baidu