5

Flightクラス メソッドとインスタンス メソッドの両方で呼び出されるモジュールがあるとします。includeextend、または両方を使用して、そのメソッドをクラスに取得できます。

class Bat < Mammal
  # Add Flight's class methods to Bat.
  extend Flight

  # Add Flight's instance methods to Bat.
  include Flight
  ...
end

includeに追加FlightされますBat.ancestorsが、追加されextendません。

私の質問は、モジュールとクラスでこれが異なるのはなぜですか? をサブクラス化するとMammal常にクラス メソッドとインスタンス メソッドの両方を一度に取得します。ただし、モジュールを混在させると、クラス メソッドとインスタンス メソッドの両方を一度に取得することはできませんself.included(フックまたはActiveSupport::Concernのようなものを使用しない限り)。

この違いの背後にある言語設計の問題はありますか?

4

3 に答える 3

10

と の両方Module#includeを使用して、 のインスタンス メソッドObject#extendに追加します。モジュールを考えると:ModuleObject

module Flight
    def can_fly?
        true
    end
end

Module#includeモジュールのインスタンス メソッドをクラスまたはモジュールのインスタンス メソッドに追加 (または混合) するために使用されます。

class Bat < Mammal
    include Flight
end

a = Bat.new()
a.can_fly?        # true

実際にはObject#is_a?メソッドに影響するため、次のようになります。

a.is_a? Flight     # true

Module#includeプライベート メソッドであるため、クラスまたは別のモジュールを定義するときに関数表記でのみ呼び出すことができます。

class Bat < Mammal
    self.include Flight     # NoMethodError: private method called
end

Object#extendモジュールのインスタンスメソッドをシングルトンメソッドとして、それが呼び出されたオブジェクトに追加するので、これを行うことができます:

b = Mammal.new()
b.extend Flight
b.can_fly?           # true
b.is_a? Flight       # true

c = Mammal.new()
c.can_fly?           # NoMethodError: undefined method

からのインスタンス メソッドのみbが含まれFlightます。他のMammalオブジェクトはしません。

クラス定義内で呼び出すとObject#extend、定義しているクラスの固有クラスにメソッドが追加されます。これは、クラス定義内で使用する場合の 2 つのメソッドの重要な違いです。これは、メソッドがクラス メソッドとして追加されるためです。

class Bat < Mammal
    extend Flight
end

Bat.can_fly?     # true

d = Bat.new
d.can_fly?       # NoMethodError: undefined method
于 2012-10-16T11:49:17.900 に答える
3

「 をサブクラス化するとMammal、常にクラス メソッドとインスタンス メソッドの両方を一度に取得する」

これBatは、オブジェクトとしてのクラスもMammalシングルトン クラスからインスタンス メソッドを継承しているためです。

継承グラフ

モジュールをクラスに含めると、メソッド ルックアップ チェーンが変更されます。したがって、実際にはクラスはインスタンス メソッドを継承しません。

モジュールでクラスを拡張することは、オブジェクトを拡張することと同じです。クラスは、モジュールのインスタンス メソッドをクラス インスタンス メソッド (つまり、クラス オブジェクト自体のメソッド) として取得するだけです。

于 2012-10-16T11:40:43.933 に答える
3

私はあなたの質問の一部に対処したいと思います:

includeに追加FlightされますBat.ancestorsが、追加されextendません。

拡張はインクルードと同じではないため、明らかに異なることを行います...拡張は、クラスのメタクラスのインクルードと同等であると考えることができます。

次の例を見てください。

module M
end

class A
  include M
end

# then you will see M within A's ancestors as you know
A.ancestors # => [A, M, Object...]


class B
  # the following is roughly the same as extend M:
  class <<self
    include M
  end
end

# then you will see M within B's metaclass' ancestors
MetaclassOfB = class <<B; self; end
MetaclassOfB.ancestors # => [M, Class, Module...]

したがって、extendはメタクラスのインクルードのようなものなので、拡張されたモジュールがメタクラスの祖先チェーンに表示されることがわかります...

于 2012-10-16T11:43:14.297 に答える