ruby – 通过BasicObject透明度的委托人

上下文:我试图在
Ruby中设置一个Decorator模式.由于Decorator应该将所有未知方法委托给底层对象,因此我使用了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的代码知道它正在操纵一个Decorator.我想要完全透明.

此时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

这样,我可以安全地使用class或instance_of?在我的装饰对象上,它将通过method_missing(由Delegator实现)将方法发送到底层对象.

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

那两种方法来自哪里?如果它们根本没有实现,为什么我需要取消它们?
我想我必须遗漏一些关于Ruby的对象模型的东西.

谢谢.

最佳答案 您可以通过检查方法获得提示:

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

方法的所有者是Decorator,但实际上是在#< Module:0x00000102137498>中定义的.所以有一个匿名模块来定义它.有意思……让我们来看看:

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

在Delegator和BasicObject之间再次有该模块.因此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
  # ...

所以制作了一个内核模块的副本,它没有to_s,inspect等…但仍然有class和instance_of?.它包含在Delegator中,这就是它们的来源.

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

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

这在Object doc中说明:

Object mixes in the Kernel module, making the built-in kernel
functions globally accessible. Although the instance methods of Object
are defined by the Kernel module, we have chosen to document them here
for clarity.

点赞