何时使用Ruby模块,而不是使用类组合?

10

之前有类似的问题被问过,但我特别询问使用组合作为替代模块混入的方法。

class Helper
  def do_somthing
  end
end
如果我需要“使用”一个类但不继承它,我会简单地组合它并使用它。
class MyStuff
  def initialize
    helper = Helper.new
    helper.do_something
  end
end

为什么我想要为此创建一个模块:

 module Helper
   def do_something
   end
 end

class MyStuff
  include Helper
end

我看到的唯一区别是,如果我使用模块,就不会有许多Helper对象。但我没有看到更多对象与较少更大的对象相比有什么不同。

此外,我不知道将来是否需要对其进行子类化。那么我如何决定我的库的用户想要使用模块混合,还是想要使用组合?


2
这里不需要 require,你需要使用 include - Linuxios
4个回答

19

Helper 类和 MyStuff 类之间的关系是拥有关系时,使用组合。这被称为"has-a"关系。例如,假设你有一个Person类和一个Car类。你会使用组合,因为一个人有一辆车。

class Person
  def initialize
    @car = Car.new
  end
end

class Car
  def accelerate
    # implementation
  end
end

Helper "像" MyStuff 一样时,使用一个模块混合。在这种情况下,Helper 扮演了MyStuff角色。这与"is-a"关系有些不同,后者意味着你应该使用传统继承。例如,假设我们有一个Person类和一个Sleeper模块。人有时会扮演睡眠者的角色,但其他对象也可以 - 比如DogFrog的实例,或者甚至是Computer。每个这样的类都代表着能够睡眠的东西。

module Sleeper
  def go_to_sleep
    # implementation
  end
end

class Person
  include Sleeper
end

class Computer
  include Sleeper
end

Sandi Metz的实践Ruby面向对象设计是这些主题的绝佳资源。


2
这是关于“鸭子类型”的问题。如果你想让你的类像Helper一样工作,你需要使用include。如果你要封装Helper行为,正确的选择是require
通过混入{{link3:Enumerable}},你的类将拥有大量方法,因为它只实现了each方法。通过包装Array,你可以隐藏迭代过程并仅用它来保存数据。反之亦然。

你能稍微解释一下吗?通过实现only each方法 - Arup Rakshit
1
你的类需要实现 only one (each) 方法并混入 Enumerable,以产生所有迭代的乐趣。 - Aleksei Matiushkin
@mudasobwa,我想你的意思是:“迭代/迭代”? - Alex

1
模块混合更像是多重继承,因此遵循通常的继承与组合规则——is-a或has-a。顺便说一下,它是include Helper,而不是require 'Helper'

是的,我同意你的观点。我认为 mixins 最好的使用场景就是实现多重继承。 - João Ramires

0

我可以分享一些事情:

  • 如果您需要在不同的类和模块中共享常见行为,则应将其制作成模块,以便稍后可以在任何地方包含模块。这也有助于测试,因为它已经是DRY。

  • 在责任方面,您可以将其制作成新模块,以便清晰理解并提高代码库的可读性。这将有助于减少主要类的大小,使其易于跟踪和维护。

您可能会注意到一件事,即include将功能添加到您的instance中,而extend则添加到Class本身。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接