上下文:我试图在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实现)将方法发送到底层对象。
问题是:我不明白为什么我必须取消:class
和:instance_of?
。我可以看到BasicObject定义了:==
,所以我不得不取消它,但那两个呢?
我查看了BasicObject文档和C代码中的一些内容,但没有找到任何内容。我在Delegator文档和代码中看起来一样,也没有找到任何东西。
似乎Delegator包含内核模块,但内核#class还是内核#instance_of?不存在。
这两种方法来自哪里?如果它们根本没有实现,为什么我需要取消它们? 我想我必须遗漏一些关于Ruby的对象模型的东西。
感谢。
答案 0 :(得分:2)
您可以通过检查方法获得提示:
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
和{{1 }}。它包含在instance_of?
中,而这就是它们的来源。
请注意,Delegator
通过包含Object
模块继承了相同的方法(当然,它包括完整的模块):
Kernel
这在Object
doc:
对象在内核模块中混合,构成内置内核 功能全局可访问。虽然是Object的实例方法 由内核模块定义,我们选择在此处记录它们 为清楚起见。