上下文:我试图在
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.