我正在阅读 Ruby,并了解了它的 mixins 模式,但想不出很多有用的 mixin 功能(因为我很可能不习惯这样思考)。所以我想知道什么是有用的 Mixin 功能的好例子?
谢谢
编辑:一点背景。我来自 C++ 和其他对象语言,但我怀疑 Ruby 说它没有继承 mixins,但我一直将 mixins 视为多重继承,所以我担心我试图过早地将它们归类到我的舒适区,并没有真正理解什么是mixin。
我正在阅读 Ruby,并了解了它的 mixins 模式,但想不出很多有用的 mixin 功能(因为我很可能不习惯这样思考)。所以我想知道什么是有用的 Mixin 功能的好例子?
谢谢
编辑:一点背景。我来自 C++ 和其他对象语言,但我怀疑 Ruby 说它没有继承 mixins,但我一直将 mixins 视为多重继承,所以我担心我试图过早地将它们归类到我的舒适区,并没有真正理解什么是mixin。
它们通常用于向类添加某种形式的标准功能,而无需重新定义它。您可能会认为它们有点像 Java 中的接口,但不仅仅是定义需要实现的方法列表,它们中的许多实际上将通过包含模块来实现。
标准库中有几个例子:
Singleton - 可以混入任何类以使其成为单例的模块。初始化方法是私有的,并添加了一个实例方法,以确保在您的应用程序中只有一个该类的实例。
Comparable - 如果你在一个类中包含这个模块,定义 <=> 方法,它将当前实例与另一个对象进行比较并说明哪个更大,足以提供 <、<=、==、>=、> 和之间?方法。
Enumerable - 通过在此模块中混合并定义每个方法,您可以获得对所有其他相关方法的支持,例如收集、注入、选择和拒绝。如果它也有 <=> 方法,那么它也将支持排序、最小值和最大值。
DataMapper 也是一个有趣的例子,它可以通过简单的 include 语句完成,采用标准类,并添加将其持久化到数据存储的能力。
好吧,我认为通常的例子是持久性
module Persistence
def load sFileName
puts "load code to read #{sFileName} contents into my_data"
end
def save sFileName
puts "Uber code to persist #{@my_data} to #{sFileName}"
end
end
class BrandNewClass
include Persistence
attr :my_data
def data=(someData)
@my_data = someData
end
end
b = BrandNewClass.new
b.data = "My pwd"
b.save "MyFile.secret"
b.load "MyFile.secret"
想象一下这个模块是由一个 Ruby 忍者编写的,它将你的类的状态保存到一个文件中。
现在假设我编写了一个全新的类,我可以通过将其混合来重用持久性的功能include ModuleILike
。您甚至可以在运行时包含模块。我只需将其混合即可免费获得加载和保存方法。这些方法就像您为自己的班级编写的方法一样。代码/行为/功能——无需继承即可重用!
所以你正在做的是将方法包含到你的类的方法表中(不是字面上正确但很接近)。
在 ruby 中,mixin 不是多重继承的原因是组合 mixin 方法是一次性的。这不会是一个大问题,除了 Ruby 的模块和类可以修改。这意味着如果您将一个模块混合到您的类中,然后向该模块添加一个方法,则该方法将不适用于您的类;如果您以相反的顺序执行此操作,则可以。
这就像订购冰淇淋蛋筒一样。如果你得到巧克力糖屑和太妃糖碎作为你的混合物,然后带着你的蛋筒走开,如果有人在冰淇淋店的巧克力糖屑箱中添加彩色糖屑,你将不会改变什么样的冰淇淋蛋筒。当 mixin 模块,洒水箱被修改时,你的类冰淇淋蛋筒没有被修改。下一个使用该 mixin 模块的人将看到这些更改。
当您include
在 ruby 中使用模块时,它会调用Module#append_features
该模块,该模块会将该模块的方法的副本添加到包含器中。
据我了解,多重继承更像是委托。如果您的班级不知道如何做某事,它会询问其父母。在开放班级环境中,班级的父母可能在班级创建后已被修改。
这就像 RL 的父子关系。你的母亲可能在你出生后就学会了如何玩杂耍,但如果有人要求你玩杂耍,而你要求她:教你怎么玩(当你需要的时候复制它)或为你做(纯粹的委托),那么她'LL能够在那一点,即使你在她的歌唱能力之前创造了。
您可以修改 ruby 模块“include”,使其更像多重继承,方法是修改Module#append_features
以保留包含器列表,然后使用method_added
回调更新它们,但这将是标准 Ruby 的一个重大转变,并且可能导致与其他代码一起工作时的主要问题。您可能最好创建一个Module#inherit
调用include
和处理委托的方法。
至于一个真实世界的例子,Enumerable
太棒了。如果您定义#each
并包含Enumerable
在您的类中,那么您就可以访问一大堆迭代器,而无需对每个迭代器进行编码。
它主要用于在 C++ 中使用多重继承或在 Java/C# 中实现接口。我不确定您的经验在哪里,但是如果您以前做过这些事情,那么 mixin 就是您在 Ruby 中执行它们的方式。这是一种将功能注入类的系统化方式。