Ruby 中的包含和扩展有什么区别?

发布于 2024-07-06 15:14:30 字数 646 浏览 6 评论 0原文

我刚刚了解 Ruby 元编程。 mixin/模块总是让我感到困惑。

  • include:混入指定模块方法作为目标类中的实例方法
  • extend:混入指定模块方法作为类方法目标类别中的strong>

那么主要区别只是这个还是潜伏着一条更大的龙? 例如

module ReusableModule
  def module_method
    puts "Module Method: Hi there!"
  end
end

class ClassThatIncludes
  include ReusableModule
end
class ClassThatExtends
  extend ReusableModule
end

puts "Include"
ClassThatIncludes.new.module_method       # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method            # "Module Method: Hi there!"

Just getting my head around Ruby metaprogramming. The mixin/modules always manage to confuse me.

  • include: mixes in specified module methods as instance methods in the target class
  • extend: mixes in specified module methods as class methods in the target class

So is the major difference just this or is a bigger dragon lurking?
e.g.

module ReusableModule
  def module_method
    puts "Module Method: Hi there!"
  end
end

class ClassThatIncludes
  include ReusableModule
end
class ClassThatExtends
  extend ReusableModule
end

puts "Include"
ClassThatIncludes.new.module_method       # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method            # "Module Method: Hi there!"

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

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

发布评论

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

评论(8

你列表最软的妹 2024-07-13 15:14:30

extend - 将指定模块的方法和常量添加到目标的元类(即单例类)
例如,

  • 如果您调用 Klazz.extend(Mod),现在 Klazz 具有 Mod 的方法(作为类方法)
  • 如果您调用 obj.extend(Mod),现在 obj 具有 Mod 的方法(作为实例方法),但 obj.class 的其他实例没有添加这些方法。
  • extend 是一个公共方法

include - 默认情况下,它混​​合指定模块的方法作为目标模块/类中的实例方法。
例如,

  • 如果您调用 class Klazz; 包括模组; end;,现在 Klazz 的所有实例都可以访问 Mod 的方法(作为实例方法)
  • include 是一个私有方法,因为它旨在从容器类/模块内调用。

但是,模块经常通过对 included 方法进行猴子修补来覆盖 include 的行为。 这在遗留 Rails 代码中非常突出。 来自 Yehuda Katz 的更多详细信息

关于 include 的更多详细信息,及其默认行为,假设您已运行以下代码

class Klazz
  include Mod
end
  • 如果 Mod 已包含在 Klazz 或其祖先之一中,则 include 语句无效
  • 它还包含 Mod 的常量在 Klazz 中,只要它们不冲突
  • 它允许 Klazz 访问 Mod 的模块变量,例如 @@foo@@bar
  • 如果存在循环包含,则会引发
  • ArgumentError该模块作为调用者的直接祖先(即它将 Mod 添加到 Klazz.ancestors,但 Mod 没有添加到 Klazz.superclass.superclass.superclass 的链中。因此,在 Klazz#foo 中调用 super 将在检查 Klazz 的真正超类的 foo 方法之前检查 Mod#foo。有关详细信息,请参阅 RubySpec。)。

当然,Ruby 核心文档始终是获取这些内容的最佳位置事物。 RubySpec 项目也是一个很棒的资源,因为他们精确地记录了功能。

extend - adds the specified module's methods and constants to the target's metaclass (i.e. the singleton class)
e.g.

  • if you call Klazz.extend(Mod), now Klazz has Mod's methods (as class methods)
  • if you call obj.extend(Mod), now obj has Mod's methods (as instance methods), but no other instance of of obj.class has those methods added.
  • extend is a public method

include - By default, it mixes in the specified module's methods as instance methods in the target module/class.
e.g.

  • if you call class Klazz; include Mod; end;, now all instances of Klazz have access to Mod's methods (as instance methods)
  • include is a private method, because it's intended to be called from within the container class/module.

However, modules very often override include's behavior by monkey-patching the included method. This is very prominent in legacy Rails code. more details from Yehuda Katz.

Further details about include, with its default behavior, assuming you've run the following code

class Klazz
  include Mod
end
  • If Mod is already included in Klazz, or one of its ancestors, the include statement has no effect
  • It also includes Mod's constants in Klazz, as long as they don't clash
  • It gives Klazz access to Mod's module variables, e.g. @@foo or @@bar
  • raises ArgumentError if there are cyclic includes
  • Attaches the module as the caller's immediate ancestor (i.e. It adds Mod to Klazz.ancestors, but Mod is not added to the chain of Klazz.superclass.superclass.superclass. So, calling super in Klazz#foo will check for Mod#foo before checking to Klazz's real superclass's foo method. See the RubySpec for details.).

Of course, the ruby core documentation is always the best place to go for these things. The RubySpec project was also a fantastic resource, because they documented the functionality precisely.

因为看清所以看轻 2024-07-13 15:14:30

你说的是正确的。 然而,事情还不止于此。

如果您有一个类 Klazz 和模块 Mod,在 Klazz 中包含 Mod 会给出 Klazz< 的实例/code> 访问 Mod 的方法。 或者,您可以使用 Mod 扩展 Klazz,为 Klazz 提供对 Mod 的访问权限。方法。 但您也可以使用 o.extend Mod 扩展任意对象。 在这种情况下,单个对象可以获得 Mod 的方法,即使与 o 具有相同类的所有其他对象都没有。

What you have said is correct. However, there is more to it than that.

If you have a class Klazz and module Mod, including Mod in Klazz gives instances of Klazz access to Mod's methods. Or you can extend Klazz with Mod giving the class Klazz access to Mod's methods. But you can also extend an arbitrary object with o.extend Mod. In this case the individual object gets Mod's methods even though all other objects with the same class as o do not.

一抹微笑 2024-07-13 15:14:30

这是正确的。

在幕后, include 实际上是 append_features 的别名,它(来自文档):

Ruby 的默认实现是
添加常量、方法和模块
该模块的变量到 aModule if
该模块尚未添加
到一个模块或其祖先之一。

That's correct.

Behind the scenes, include is actually an alias for append_features, which (from the docs):

Ruby's default implementation is to
add the constants, methods, and module
variables of this module to aModule if
this module has not already been added
to aModule or one of its ancestors.

最后的乘客 2024-07-13 15:14:30

当您将模块包含到类中时,模块方法将作为实例方法导入。

但是,当您将模块扩展为类时,模块方法将作为类方法导入。

例如,如果我们有一个模块 Module_test 定义如下:

module Module_test
  def func
    puts "M - in module"
  end
end

现在,对于 include 模块。 如果我们按如下方式定义类 A

class A
  include Module_test
end

a = A.new
a.func

输出将为:M - in module

如果我们将 include Module_test 行替换为 extend Module_test 并再次运行代码,我们会收到以下错误:undefined method 'func' for #(无方法错误)

将方法调用 a.func 更改为 A.func,输出更改为:M - in module

从上面的代码执行中可以清楚地看出,当我们包含一个模块时,它的方法会变成实例方法,而当我们扩展一个模块,它的方法成为类方法

When you include a module into a class, the module methods are imported as instance methods.

However, when you extend a module into a class, the module methods are imported as class methods.

For example, if we have a module Module_test defined as follows:

module Module_test
  def func
    puts "M - in module"
  end
end

Now, for include module. If we define the class A as follows:

class A
  include Module_test
end

a = A.new
a.func

The output will be: M - in module.

If we replace the line include Module_test with extend Module_test and run the code again, we receive the following error: undefined method 'func' for #<A:instance_num> (NoMethodError).

Changing the method call a.func to A.func, the output changes to: M - in module.

From the above code execution, it is clear that when we include a module, its methods become instance methods and when we extend a module, its methods become class methods.

与风相奔跑 2024-07-13 15:14:30

所有其他答案都很好,包括深入研究 RubySpecs 的技巧:

https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb

https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb

至于用例:

如果您包括 类 ClassThatInincludes 中的模块 ReusableModule、方法、常量、类、子模块和其他声明都被引用。

如果您使用模块 ReusableModule 来扩展类 ClassThatExtends,那么方法和常量就会被复制。 显然,如果您不小心,动态复制定义可能会浪费大量内存。

如果您使用 ActiveSupport::Concern,.included() 功能可让您直接重写包含类。 关注点内的模块 ClassMethods 被扩展(复制)到包含的类中。

All the other answers are good, including the tip to dig through RubySpecs:

https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb

https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb

As for use cases:

If you include module ReusableModule in class ClassThatIncludes, the methods, constants, classes, submodules, and other declarations gets referenced.

If you extend class ClassThatExtends with module ReusableModule, then the methods and constants gets copied. Obviously, if you are not careful, you can waste a lot of memory by dynamically duplicating definitions.

If you use ActiveSupport::Concern, the .included() functionality lets you rewrite the including class directly. module ClassMethods inside a Concern gets extended (copied) into the including class.

梦里°也失望 2024-07-13 15:14:30

我还想解释一下其运作机制。 如果我说得不对请指正。

当我们使用 include 时,我们添加了从类到包含一些方法的模块的链接。

class A
include MyMOd
end

a = A.new
a.some_method

对象没有方法,只有类和模块有。
因此,当a收到消息some_method时,它开始在a的特征类中搜索方法some_method,然后在>A 类,然后链接到 A 类模块(如果有)(按相反顺序,最后包含的获胜)。

当我们使用extend时,我们正在添加到对象的特征类中的模块的链接。
因此,如果我们使用 A.new.extend(MyMod),我们会将模块的链接添加到 A 的实例特征类或 a' 类。
如果我们使用 A.extend(MyMod),我们将添加到 A(对象,类也是对象)eigenclass A' 的链接。

所以a的方法查找路径如下:
一个=> 一个' => 将模块链接到“类=>” A.

还有一个改变查找路径的前置方法:

a => 一个' => A => 前面的模块 A=> 包含模块,抱歉

我的英语不好。

I would also like to explain the mechanism as it works. If I am not right please correct.

When we use include we are adding a linkage from our class to a module which contains some methods.

class A
include MyMOd
end

a = A.new
a.some_method

Objects don't have methods, only clases and modules do.
So when a receives mesage some_method it begin search method some_method in a's eigen class, then in A class and then in linked to A class modules if there are some (in reverse order, last included wins).

When we use extend we are adding linkage to a module in object's eigen class.
So if we use A.new.extend(MyMod) we are adding linkage to our module to A's instance eigen class or a' class.
And if we use A.extend(MyMod) we are adding linkage to A(object's, classes are also objects) eigenclass A'.

so method lookup path for a is as follows:
a => a' => linked modules to a' class => A.

also there is a prepend method which changes lookup path:

a => a' => prepended modulesto A => A => included module to A

sorry for my bad english.

内心旳酸楚 2024-07-13 15:14:30

我发现了一篇非常有用的文章比较类内使用的 includeextendprepend 方法:

include 添加模块方法作为类的实例方法,而 extend 将模块方法添加为类方法。 必须相应地定义所包含或扩展的模块

I came across a very useful article that compares include, extend and prepend methods used inside a class:

include adds module methods as instance methods to the class, whereas extend adds module methods as class methods. The module being included or extended must be defined accordingly

秋日私语 2024-07-13 15:14:30

include 为类提供对模块方法的访问作为实例

methods
module Plant
  def poisonous?
    true
  end
end

class PlantKingdom
end

class Hemlock < PlantKingdom
  include Plant
end

obj = Hemlock.new()

obj.poisonous?
=> true

Hemlock.poisonous?
=> NoMethodError: undefined method `poisonous?' for Hemlock:Class

extend 为类提供对模块方法的访问作为类方法

module Plant
  def poisonous?
    true
  end
end

class PlantKingdom
end

class Hemlock < PlantKingdom
  extend Plant
end

obj = Hemlock.new()

obj.poisonous?
=> NoMethodError: undefined method `poisonous?' for #<Hemlock:0x00007fa84389c748>

Hemlock.poisonous?
=> true

include provides a class with access to a module's methods as an instance

methods
module Plant
  def poisonous?
    true
  end
end

class PlantKingdom
end

class Hemlock < PlantKingdom
  include Plant
end

obj = Hemlock.new()

obj.poisonous?
=> true

Hemlock.poisonous?
=> NoMethodError: undefined method `poisonous?' for Hemlock:Class

extend provides a class with access to a module's methods as class methods

module Plant
  def poisonous?
    true
  end
end

class PlantKingdom
end

class Hemlock < PlantKingdom
  extend Plant
end

obj = Hemlock.new()

obj.poisonous?
=> NoMethodError: undefined method `poisonous?' for #<Hemlock:0x00007fa84389c748>

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