V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Mark24
V2EX  ›  Ruby

Ruby 的方法查找再往前一步

  •  
  •   Mark24 · 2021-07-29 18:51:58 +08:00 · 2218 次点击
    这是一个创建于 1201 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有更好的方法可以告诉我,我最新在学习 Ruby

    最新的修改会更新在 BLOG

    我的博客

    RubyChina 讨论帖


    背景

    《 Ruby 元编程(第二版)》 5.4 节 单件类 在 Page125 这页,讲了一种情况:

    
    class C
      def a_method
        'C#a_method()'
      end
    end
    
    
    
    class C
      class << self
        def a_class_method
          '#C.a_class_method() #singleton'
        end
      end
    end
    
    class D < C;end
    
    obj = D.new
    
    
    D.a_class_method # => '#C.a_class_method() #singleton'
    

    D.a_class_method 他是如何查找的呢?

    本文就是寻找这个的答案。讲的是 Ruby 的方法查找再往前走一步。

    为了说明这个问题,先要啰嗦的做一些铺垫。

    下文中,此书简称为《元编程》

    一、Ruby 的继承结构

    这张图实在总结的太美丽了。先放在这里。图片的出处,可以参考文末。

    二、Ruby 一般方法查找规则

    《元编程》里面里面总结了 Ruby 的查找规则:

    “向右一步,然后向上查找”。

    意思就是,向右寻找他的父,然后开始往上寻找继承关系,通过这种方式查找方法。

    比如以下代码

    class C
      def a_method
        'C#a_method()'
      end
    end
    
    
    class D < C;end
    
    obj = D.new
    obj.a_method
    

    obj 如何查找 a_method 方法呢?

    如果我们给 obj 对象添加单例类,他会如何查找呢?

    
    class C
      def a_method
        'C#a_method()'
      end
    end
    
    
    class D < C;end
    
    obj = D.new
    
    # 定义单例类
    
    class << obj
      def a_singleton_method
        "obj#a_singleton_method"
      end
    end
    
    obj.a_singleton_method 
    
    

    obj.a_singleton_method 会如何查找方法呢?

    可以通过一下方式检验

    
    obj.singleton_class.superclass # => D
    
    

    他会按照如图的方式,其实实例对象创造了一个 单例类 可以标记为 #obj,用#表示单例类。 #obj 会出现在对象和真正的类中间。

    我们也能用上面

    “向右一步,然后向上查找”。

    来指导我们查找,只不过对象存在一个单例类罢了。

    三、新的问题出现

    但是问题来了,回到文章的最开头。

    
    class C
      def a_method
        'C#a_method()'
      end
    end
    
    
    
    class C
      class << self
        def a_class_method
          '#C.a_class_method() #singleton'
        end
      end
    end
    
    class D < C;end
    
    obj = D.new
    
    
    D.a_class_method # => '#C.a_class_method() #singleton'
    

    这个例子。在类 C 上定义了单例方法,并且我们指导所有东西在 Ruby 里都是对象,都可以定义单例方法。

    这就是文章开头最先的图片。所有的类都可以定义单例类。这种情况下,D.a_class_method 应该如何查找呢?

    “向右一步,然后向上查找”。

    似乎帮不了我们了。因为我们面临一个问题,让我来描述下:

    我们把 D 当做一个对象,开始寻找他的方法。

    拿这幅图做例子:

    Dog 开始寻找定义的方法,向右一步,进入自己的 单例类 #Dog,然后应该做什么,选择向上么?是走 他的父类 Class,还是 应该往 单例类的继承链往上找呢?

    《元编程》文末的几句话,似乎在暗示黄色这条线的寻找方向,但是作者并没有真正说清楚:

    三、寻找答案

    我先放出答案,如下图所示:

    对象的方法,遵循

    “向右一步,然后向上查找”。

    类方法的查找是我们关心的,可以看到实际结果是,它沿着继承的单例类一路向上,然后再进入父类。

    寻找这个答案的过程中,我看了挺多资料和文字,还有问一些 Ruby 方面的朋友都没有真正分析到这一步。

    我最后是怎么找到答案的呢? 这就得借助 Ruby 自身完善的自省机制。(吐槽,其他语言可能都没有实现的那么细致)。

    其实 Ruby 自身的很多属性都绑定在自身了,直接向 Ruby 问答案就好了

    
    class C
      def a_method
        'C#a_method()'
      end
    end
    
    
    
    class C
      class << self
        def a_class_method
          '#C.a_class_method() #singleton'
        end
      end
    end
    
    class D < C;end
    
    obj = D.new
    
    
    D.a_class_method # => '#C.a_class_method() #singleton'
    

    我们知道 obj.ancestors 可以打印继承关系,但是这个很遗憾的是它不会打印 单例类。

    单例类实际上是一个隐藏的存在。这也就是研究这个问题很难得地方,因为隐藏,似乎只能通过源码和外部资料去查看。

    实际上我们是可以拿到 obj.singleton_class 的,然后我们前面分析了一些结论,大致给出了一个对象的继承模型。

    obj.singleton_class.ancestors
    
    # => [#<Class:#<D:0x00007feae092efc8>>, D, C, Object, Kernel, BasicObject]
    

    就可以打印出,对象查找的顺序。

    同理,我们想要知道 D 的方法的查找顺序

    D.singleton_class.ancestors
    
    # => [#<Class:D>, #<Class:C>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
    

    这个其实就是 D 查找方法的顺序,可以看到,他先是把所有的单例类走了一遍,然后开始进入自己的父。

    四、总结

    最后,这句话

    “向右一步,然后向上查找”。

    有了新内涵, 向右一步的过程中,优先的走单例类(如果有的话)以及单例的继承,结束后,开始进入自己真正的父,即向上在继承关系中寻找。

    单例类也可以看成是一种外挂方法(比喻不严谨但是很好理解),先在外挂方法里找,也可以顺着外挂继承链找。找不到再到继承关系里面找。

    题外话

    有人可能会问,为啥继承体系要搞得那么复杂?

    借用 《元编程》里面的一句

    这样你就可以在 D 中 调用 C 的方法了。

    把对象穿成链表,然后相当于你可以拥有和复用这个链条上所有的方法。

    元编程的一部分思想也就是动态的修改、创造、转发方法。还有 《元编程》里面提到的 “自由方法”我的理解就像是把继承链中某些方法复制,然后粘贴到当前对象执行,在继承链上跳跃执行方法……

    这一切都是为了极大地自由。

    我以前一致不太理解“Ruby 是快乐优先”这句话是什么意思,现在我的理解——这种快乐就是自由,拥有自由的快乐。

    其他

    图片来自文章

    安利一波作者图片配色

    参考

    书籍推荐 《 Ruby 元编程(第 2 版)》

    BLOG

    有更好的方法可以告诉我,我最新在学习 Ruby

    最新的修改会更新在 BLOG

    我的博客

    RubyChina 讨论帖

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3406 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 11:41 · PVG 19:41 · LAX 03:41 · JFK 06:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.