如何让模块 mixins 适用于静态方法?

发布于 2024-10-01 14:13:53 字数 279 浏览 3 评论 0原文

假设我有两个模块。是否可以将一个模块包含在另一个模块中,使其表现得像 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

编辑:我意识到我最初复制的代码是错误的。这些方法必须是静态的。更正后的代码在上面(并且不起作用)。

Lets say I have two modules. Is it possible to include a module inside another one that would behave like a mixin?

For example:

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

Edit: I realized I originally copied the code down wrong. The methods need to be static. The corrected code is above(and does not work).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

孤凫 2024-10-08 14:13:53

正如您所知,它不起作用,但为什么它不起作用是关于 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< 制作的精彩免费视频/a> (不,不是温迪店里的那个人):
http://scotland-on-rails.s3.amazonaws.com/2A04_DaveThomas -SOR.mp4

观看该视频后,我购买了 Dave Thomas 的整个 Pragmatic 的 Ruby 对象模型系列,非常值得。

PS 如果我忘记了任何内容,请随时纠正我;就像物体中具体的东西一样。

As you've learned it doesn't work but why it doesn't work is a really good lesson about the Ruby object model.

When you create an instance of an object what you have created is a new object with a set of instance variables and a pointer to the class of the object (and a few other things like an object ID and a pointer to the superclass) but the methods themselves are not in the instance of the object. The class definition contains the list of methods and their code (and a pointer to its own class, a pointer to its superclass, and an object ID).

When you call a method on an instance Ruby looks up the class of the instance and looks in that class's method list for the method you called. If it doesn't find it then it looks in the class' superclass. If it doesn't find it there it looks in that class' superclass until it runs out of superclasses. Then it goes back to the first class and looks for a method_missing method. If it doesn't find one it goes to the superclass and so on till it gets to the root object where it's designed to raise an error.

Let's say for instance you have a class Person and you make an instance of the class with the variable bubba like this:

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)

The class diagram would look something like this:
alt text

So what's happening when you create a static method, a class/module method? Well, remember that almost everything is an object in Ruby and a module definition is an instance of the class Class. Yep, that code you type out is actually an instance too, it's live code. When you create a class method by using def self.method_name you are creating a method in the instance of the object that is the class/module definition.

Great, so where's that class method being defined at you ask? It's being defined in an anonymous class (aka singleton, eigen, ghost class) that is created for exactly this reason.

Going back to our Person class what if we add a class method on the instance bubba like so:

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

That method gets put into a special singleton class created just for that instance and the singleton's superclass is now the Person class. This makes our method calling chain look like this:
alt text

Any other methods defined on the instance object bubba will also be put into that singleton class. There's never more than one singleton class per instance object.

So, to wrap it all up the reason why it doesn't work is the static methods in the modules are being defined in the singleton class for the instance of the module definition. When you include or extend from the module you are adding a pointer to the method table of the module but not the method table of the instance object of the singleton class for the module.

Think of it this way: If you create an instance x of type Z and an instance y of type Z should x know about y? No, not unless specifically told about it. So too your module that mixes in another module should not know about some other object that just happens to have that first module as its superclass.

For a much better explanation of the Ruby object model watch this awesome free video by the amazingly erudite Dave Thomas (no, not the guy from Wendy's):
http://scotland-on-rails.s3.amazonaws.com/2A04_DaveThomas-SOR.mp4

After watching that video I bought Dave Thomas's whole series on the Ruby object model from Pragmatic and it was well worth it.

P.S. Anyone please feel free to correct me on anything I forgot; like what's specifically in an object.

我不吻晚风 2024-10-08 14:13:53

使用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

Use extend instead of include to add class methods.

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
乖不如嘢 2024-10-08 14:13:53

您发布的确切代码的工作原理与您想要的完全一样。所以,答案是肯定的。

自己去执行真的那么难吗?

The exact code you posted works exactly like you want to. So, the answer is Yes.

Would it have been really that hard to just execute it yourself?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文