2012-07-09 35 views
4

上下文:我試圖在Ruby中搭建一個Decorator模式。作爲裝飾者應該將所有未知方法委託給底層對象,我使用了Delegator類。 我可以使用SimpleDelegator,但我想完全理解我在做什麼。通過BasicObject透明度的Delegator

所以我就出來了基本的代碼是:

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實現)傳遞給底層對象。

事情是:我不明白爲什麼我必須undef :class:instance_of?。我可以看到BasicObject定義了:==,所以我不得不取消定義它,但那兩個呢? 我查看了BasicObject文檔,並在C代碼中找了一下,但沒有找到任何東西。我在Delegator文檔和代碼上看起來一樣,但是也沒有找到任何東西。 看來Delegator包含內核模塊,但是Kernel#class或者Kernel#instance_of?不存在。

這兩種方法來自哪裏?爲什麼我需要不定義它們,如果它們根本沒有實現的話? 我想我必須錯過一些關於Ruby的對象模型的東西。

謝謝。

回答

2

您可以通過檢查方法得到一個提示:

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指出在內核模塊中,使內置的內核 可以全局訪問。儘管對象 的實例方法由內核模塊定義,但爲了清楚起見,我們選擇將它們記錄在此處 。

+0

所以你說'class'&'instance_of?'來自內核模塊?我無法在內核模塊文檔Oo中找到這些方法:http://www.ruby-doc.org/core-1.9.3/Kernel.html。謝謝。 – 2012-07-10 11:58:56

+0

是的。我會在Kernel doc中添加一行來說明這一點。 – 2012-07-10 12:17:53

+0

好的,謝謝:) – 2012-07-10 12:46:20