4

コンテキスト: 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?。メソッドは、(Delegator によって実装されている) method_missing を介して基になるオブジェクトに送信されます。

問題は、なぜ undef:class:instance_of?. BasicObject が定義していることがわかる:==ので、未定義にする必要がありましたが、これら 2 つについてはどうでしょうか。BasicObject のドキュメントと C コードを少し調べましたが、何も見つかりませんでした。Delegator のドキュメントとコードも同じように調べましたが、何も見つかりませんでした。Delegator には Kernel モジュールが含まれているようですが、Kernel#class または Kernel#instance_of? 存在しません。

これらの2つの方法はどこから来たのですか? それらがまったく実装されていないのに、なぜそれらを未定義にする必要があったのでしょうか? Rubyのオブジェクトモデルか何かについて何かが欠けているに違いないと思います。

ありがとう。

4

1 に答える 1

3

メソッドを調べることでヒントを得ることができます。

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、とは含まれています。それはに含まれていて、そこから来ています。inspectclassinstance_of?Delegator

Objectモジュールを含めることで同じメソッドを継承することに注意してくださいKernel(ただし、もちろん、モジュール全体が含まれます)。

42.method(:class) # => #<Method: Fixnum(Kernel)#class>

これはObjectドキュメントに記載されています:

カーネルモジュールでオブジェクトが混在し、組み込みのカーネル機能にグローバルにアクセスできるようにします。ObjectのインスタンスメソッドはKernelモジュールによって定義されていますが、わかりやすくするためにここでそれらを文書化することを選択しました。

于 2012-07-10T03:44:55.797 に答える