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

时间:2010-11-09 19:56:57

标签: 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

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

3 个答案:

答案 0 :(得分: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)

类图看起来像这样: alt text

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

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

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

def bubba.drive_pickup
  puts "Yee-haw!"
end

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

实例对象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的整个series on the Ruby object model,这非常值得。

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

答案 1 :(得分: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

答案 2 :(得分:-4)

您发布的确切代码的工作方式与您想要的完全相同。所以答案是肯定的。

自己执行它真的很难吗?