【问题标题】:Delegator through BasicObject transparency通过 BasicObject 透明度委托
【发布时间】:2012-07-10 20:17:22
【问题描述】:

上下文:我正在尝试在 Ruby 中建立一个装饰器模式。由于装饰器应该将所有未知方法委托给底层对象,我使用了 Delegator 类。 我本可以使用 SimpleDelegator,但我想完全了解我在做什么。

所以我得出的基本代码是:

class Decorator < Delegator
  def initialize(component)
    super
    @component = component
  end

  def __setobj__(o); @component = o   end
  def __getobj__;    @component       end
  def send(s, *a);   __send__(s, *a)  end
end

这与 SimpleDelegator 的实现完全相同。看起来不错。

但我不想让处理装饰器的代码知道它正在操纵装饰器。我想要完全透明。

此时Decorator.new(Object.new).class返回Decorator

所以我做了一些修改并想出了这个:

class Decorator < Delegator
  undef_method :==
  undef_method :class
  undef_method :instance_of?

  # Stores the decorated object
  def initialize(component)
    super
    @component = component
  end

  def __setobj__(o); @component = o   end
  def __getobj__;    @component       end
  def send(s, *a);   __send__(s, *a)  end
end

这样,我可以安全地在我的 Decorated 对象上使用 classinstance_of?,它会通过 method_missing(由 Delegator 实现)将方法发送到底层对象。

问题是:我不明白为什么我必须取消定义:class:instance_of?。我可以看到 BasicObject 定义了 :== 所以我不得不取消定义它但是那两个呢? 我查看了 BasicObject 文档和 C 代码中的一些内容,但没有找到任何东西。我查看了 Delegator 文档和代码,也没有找到任何东西。 似乎 Delegator 包含 Kernel 模块,但是 Kernel#class 还是 Kernel#instance_of?不存在。

这两种方法从何而来?如果它们根本没有实施,为什么我需要取消定义它们? 我想我一定是遗漏了一些关于 Ruby 的对象模型之类的东西。

谢谢。

【问题讨论】:

    标签: ruby delegates decorator


    【解决方案1】:

    你可以通过检查方法得到一个提示:

    Decorator.instance_method(:class)
      # =>  #<UnboundMethod: Decorator(#<Module:0x00000102137498>)#class> 
    

    方法的所有者是Decorator,但实际上是在#&lt;Module:0x00000102137498&gt; 中定义的。所以有一个匿名模块来定义它。有趣...让我们看看:

    Decorator.ancestors
      # => [Decorator, Delegator, #<Module:0x00000102137498>, BasicObject] 
    

    又是那个模块,在DelegatorBasicObject 之间。所以Delegator 不直接派生自BasicObject。如果您查看lib/delegate.rb 中的源代码,您会发现:

    class Delegator < BasicObject
      kernel = ::Kernel.dup
      kernel.class_eval do
        [:to_s,:inspect,:=~,:!~,:===,:<=>,:eql?,:hash].each do |m|
          undef_method m
        end
      end
      include kernel
      # ...
    

    因此制作了Kernel 模块的副本,其中没有to_sinspect 等...但仍然有classinstance_of?。它包含在 Delegator 中,这就是它们的来源。

    请注意,Object 通过包含 Kernel 模块继承了相同的方法(当然,它包含完整的模块):

    42.method(:class) # => #<Method: Fixnum(Kernel)#class>
    

    这在Object doc:

    中有说明

    Object 混合在 Kernel 模块中,使得内置内核 全局可访问的函数。虽然 Object 的实例方法 由内核模块定义,我们选择在此处记录它们 为了清楚起见。

    【讨论】:

    • 所以你是说class & instance_of? 来自内核模块?我在内核模块文档 Oo 中找不到这些方法:ruby-doc.org/core-1.9.3/Kernel.html。谢谢。
    • 是的。我也会在内核文档中添加一行来说明这一点。
    猜你喜欢
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    • 2011-07-14
    • 2012-12-24
    • 2016-11-02
    • 1970-01-01
    • 2021-05-11
    • 2019-09-08
    相关资源
    最近更新 更多