如何使模块mixin适用于静态方法?

jua*_*aid 5 ruby mixins

可以说我有两个模块.是否可以在另一个模块中包含一个类似mixin的模块?

例如:

module A
  def self.foo
    puts "foo"
    bar
  end
end

module B
  include A
  def self.bar
    puts "bar"
  end
end

B.bar
B.foo
Run Code Online (Sandbox Code Playgroud)

编辑:我意识到我最初错误地复制了代码.方法需要是静态的.更正的代码在上面(并且不起作用).

小智 18

正如你所知道它不起作用,但为什么它不起作用是关于Ruby对象模型的一个非常好的教训.

当你创建一个对象的实例时,你创建的是一个新对象,它有一组实例变量和一个指向对象类的指针(以及一些其他的东西,比如对象ID和指向超类的指针)但是方法本身不在对象的实例中.类定义包含方法列表及其代码(以及指向其自己的类的指针,指向其超类的指针和对象ID).

当您在实例上调用方法时,Ruby会查找实例的类,并在该类的方法列表中查找您调用的方法.如果找不到它,那么它会在类的超类中查找.如果它没有在那里找到它,它会查看该类的超类,直到它用完超类.然后它返回到第一个类并查找method_missing方法.如果没有找到它,那么它会进入超类,依此类推,直到它到达设计为引发错误的根对象.

比方说,你有一个Person类,你用变量bubba创建一个类的实例,如下所示:

class Person
  attr_accessor :dob, :name
  def age
    years = Time.now.year - @dob.year
    puts "You are #{years} year#{"s" if years != 1} old"
  end
  def feed
    puts "nom, nom, nom"
  end
end
bubba = Person.new
bubba.name = "Bubba"
bubba.dob = Time.new(1983,9,26)
Run Code Online (Sandbox Code Playgroud)

类图看起来像这样: 替代文字

那么在创建静态方法,类/模块方法时会发生什么?好吧,请记住,几乎所有东西都是Ruby中的对象,模块定义是类Class的一个实例.是的,你输入的代码实际上也是一个实例,它是实时代码.使用def self.method_name创建类方法时,您将在对象的实例中创建一个方法,该方法是类/模块定义.

太好了,那么你所要求的类方法在哪里?它是在一个匿名类(也就是单例,特征,鬼类)中定义的,正是出于这个原因而创建的.

回到我们的Person类,如果我们在实例bubba上添加类方法,如下所示:

def bubba.drive_pickup
  puts "Yee-haw!"
end
Run Code Online (Sandbox Code Playgroud)

该方法被放入一个专为该实例创建的特殊单例类中,而单例的超类现在是Person类.这使我们的方法调用链看起来像这样: 替代文字

在实例对象bubba上定义的任何其他方法也将被放入该单例类中.每个实例对象永远不会有多个单例类.

因此,为了解决这一问题的原因,模块中的静态方法是在单例类中为模块定义的实例定义的.当您从模块中包含或扩展时,您将添加指向模块的方法表的指针,但不添加模块的单例类的实例对象的方法表.

可以这样想:如果你创建一个类型为Z的实例x和一个类型为Z的实例y,那么x应该知道y吗?不,除非特别说明,否则不会.在另一个模块中混合的模块也不应该知道其他一些恰好将第一个模块作为其超类的对象.

为了更好地解释Ruby对象模型,观看这个令人敬畏的免费视频,由令人惊讶的博学的Dave Thomas(不,不是来自Wendy的人):http:
//scotland-on-rails.s3.amazonaws.com/2A04_DaveThomas-SOR .MP4

观看视频后,我从Pragmatic 购买了Dave Thomas 关于Ruby对象模型的全系列文章,这非常值得.

PS任何人都可以随意纠正我忘记的任何事情; 就像对象中的具体内容一样.


Pet*_*eus 10

使用extend而不是include来添加类方法.

module A
  module ClassMethods
    def foo
      puts "foo"
      puts bar
    end    
  end
  extend ClassMethods    
end

module B
  extend A::ClassMethods
  def self.bar
    puts "bar"
  end
end

B.bar
B.foo
Run Code Online (Sandbox Code Playgroud)