上下文 :我试图在 Ruby 中建立一个装饰器模式。由于装饰器应将所有未知方法委托(delegate)给底层对象,因此我使用了 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

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

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

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

谢谢。

最佳答案

您可以通过检查方法获得提示:

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

该方法的所有者是 Decorator 但实际上是在 #<Module:0x00000102137498> 中定义的。所以有一个匿名模块来定义它。有趣……让我们看看:
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 中有说明:

关于ruby - 通过 BasicObject 透明的委托(delegate),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11402796/

10-10 01:35