上下文 :我试图在 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
这样,我可以安全地在我的装饰对象上使用
class
或 instance_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]
在
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
# ...
因此制作了
Kernel
模块的副本,其中没有 to_s
、 inspect
等……但仍然有 class
和 instance_of?
。它包含在 Delegator
中,这就是它们的来源。请注意,
Object
通过包含 Kernel
模块继承了相同的方法(当然,它包含完整的模块):42.method(:class) # => #<Method: Fixnum(Kernel)#class>
这在
Object
doc 中有说明:关于ruby - 通过 BasicObject 透明的委托(delegate),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11402796/