9

Ruby 1.9.3では、いくつかのクラスインスタンスを作成する必要があります。これらのインスタンスは、それぞれ類似したインスタンスメソッドとクラスメソッドを持ちますが、いくつかの固定パラメーターによってのみ異なります。クラスタイプの区別も重要であるため、同じクラスの個別のインスタンスを単純に使用することはできません。

簡略化した例は次のようになります。

module Animal
private
  def self.make_animal(name, legs, noise)
    klass = Class.new
    klass.const_set(:NUM_LEGS, legs)
    klass.class.send(:define_method, :scream) { noise.upcase + '!' }
    Animal.const_set(name, klass)
  end
  make_animal :Tiger, 4, 'roar'
  make_animal :Human, 2, 'derp'
end

これは、「scream」メソッドを動的に定義するブロックで使用される変数が、「make_animal」メソッドの実行時ではなく「scream」メソッドの実行時にバインドされることを除いて、正常に機能するようです。

Animal::Human::NUM_LEGS # => 2 -- ok
Animal::Tiger::NUM_LEGS # => 4 -- ok
Animal::Human.scream # => "DERP!" -- ok
Animal::Tiger.scream # => "DERP!" -- fail!

Tigerが悲鳴を上げるように、上記のコードを変更するにはどうすればよい"ROAR!"ですか?

[注]ここで説明するには複雑すぎる理由から、例では間抜けなOO構造を維持する必要があります。パラメータ化されたメソッド実装を使用して動的に定義されたクラスでクラスメソッドをプログラムで定義する方法を学ぶことにのみ興味があります。

4

2 に答える 2

12

klass.classどちらの場合も同じです(クラス):すべてのクラスはのインスタンスですClass。その結果、あなたは悲鳴を定義し、それを再定義しています。

ルビーのクラスメソッドとしてよく考えられるのは、実際にはシングルトンメソッドです(興味がある場合は、固有クラスなどについて読むべきことがたくさんあります)。

The

def some_object.foo
end

コンストラクトはシングルトンメソッドを作成します。多くの場合、これはselfを使用してクラス定義内にありますが、たとえば、

x = 'dog'
def x.bark
  "Woof"
end

次に、x.barkはwoofを返しますが、barkは他の文字列では定義されません。

ここでは、メソッドが変数を参照する必要があるため、を使用してメソッドを定義するnoise必要があります。define_singleton_method

まだruby1.8を使用している場合は、define_singleton_methodを使用できません。シングルトンメソッドは固有クラスのメソッドであるという事実を使用する必要があります。

klass = Class.new
eigenclass = class << klass; self; end
eigenclass.send(:define_method, :scream){noise}

define_singleton_methodを使用するのと同じです

于 2012-12-12T21:14:24.830 に答える
4

あなたのコードの問題は間違ったバインディングの瞬間ではありません。でメソッドを定義しているということですClass#class。そして、それは、驚き-驚き、、Class唯一無二です。つまり、「roar」バージョンを「derp」バージョンで上書きしていることになります。

代わりに、これらの動的クラスのメソッドを直接定義する必要があります。これが私の見解です(インスタンスvarを使用していますがnoise、問題がないことを願っています)。

module Animal
private
  def self.make_animal(name, legs, noise)
    klass = Class.new
    klass.const_set(:NUM_LEGS, legs)
    klass.instance_variable_set(:@noise, noise)

    klass.instance_eval do |k|
      def scream
        @noise.upcase + '!'
      end
    end

    Animal.const_set(name, klass)
  end

  make_animal :Tiger, 4, 'roar'
  make_animal :Human, 2, 'derp'
end

Animal::Human::NUM_LEGS # => 2
Animal::Tiger::NUM_LEGS # => 4

Animal::Human.scream # => "DERP!"
Animal::Tiger.scream # => "ROAR!"
于 2012-12-12T20:18:24.057 に答える