我来自 Java,现在我更多地使用 Ruby。
我不熟悉的一种语言功能是 module
。我想知道 module
到底是什么,你什么时候使用它,为什么使用 module
而不是 class
?
╔═══════════════╦═══════════════════════════╦═════════════════════════════════╗
║ ║ class ║ module ║
╠═══════════════╬═══════════════════════════╬═════════════════════════════════╣
║ instantiation ║ can be instantiated ║ can *not* be instantiated ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ usage ║ object creation ║ mixin facility. provide ║
║ ║ ║ a namespace. ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ superclass ║ module ║ object ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ methods ║ class methods and ║ module methods and ║
║ ║ instance methods ║ instance methods ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ inheritance ║ inherits behaviour and can║ No inheritance ║
║ ║ be base for inheritance ║ ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ inclusion ║ cannot be included ║ can be included in classes and ║
║ ║ ║ modules by using the include ║
║ ║ ║ command (includes all ║
║ ║ ║ instance methods as instance ║
║ ║ ║ methods in a class/module) ║
╟───────────────╫───────────────────────────╫─────────────────────────────────╢
║ extension ║ can not extend with ║ module can extend instance by ║
║ ║ extend command ║ using extend command (extends ║
║ ║ (only with inheritance) ║ given instance with singleton ║
║ ║ ║ methods from module) ║
╚═══════════════╩═══════════════════════════╩═════════════════════════════════╝
第一个答案很好,并给出了一些结构性的答案,但另一种方法是考虑你在做什么。模块是关于提供可以跨多个类使用的方法——将它们视为“库”(就像您在 Rails 应用程序中看到的那样)。类是关于对象的;模块是关于功能的。
例如,身份验证和授权系统就是很好的模块示例。身份验证系统跨多个应用程序级类工作(用户经过身份验证,会话管理身份验证,许多其他类将根据身份验证状态采取不同的行动),因此身份验证系统充当共享 API。
当您在多个应用程序之间共享方法时,您也可以使用模块(同样,库模型在这里很好)。
我很惊讶没有人这么说。
由于提问者来自 Java 背景(我也是),所以这里有一个有帮助的类比。
类就像 Java 类一样。
模块就像 Java 静态类。想想 Java 中的 Math
类。您无需实例化它,而是重用静态类中的方法(例如 Math.random()
)。
extend self
),使其方法可用于其 self
的元类。这使得在 Math
模块上分派像 random()
这样的方法成为可能。但就其本质而言,模块的方法不能在模块自己的 self
上调用。这与 Ruby 的 self
概念、它的元类以及方法查找的工作方式有关。查看“元编程 Ruby”——Paolo Perlotta 了解详细信息。
基本上,模块不能被实例化。当一个类包含一个模块时,会生成一个代理超类,它提供对所有模块方法以及类方法的访问。
一个模块可以包含在多个类中。模块不能被继承,但是这种“mixin”模型提供了一种有用的“多重继承”类型。 OO 纯粹主义者会不同意这种说法,但不要让纯粹性妨碍完成工作。
(此答案最初链接到 http://www.rubycentral.com/pickaxe/classes.html
,但该链接及其域不再有效。)
extend
类来采用类似“静态”的方法。 Ruby 实际上根本不区分“实例”和“类/静态”方法,只区分它们的接收者。
命名空间:模块是命名空间......在java中不存在;)
我也从 Java 和 python 切换到 Ruby,我记得有完全相同的问题......
所以最简单的答案是模块是一个命名空间,它在 Java 中是不存在的。在 java 中,与命名空间最接近的思维方式是包。
所以 ruby 中的模块就像 java 中的那样:类?没有接口?没有抽象类?没有包裹?也许吧)
java中类中的静态方法:与ruby中模块中的方法相同
在java中,最小单位是一个类,你不能在一个类之外拥有一个函数。然而在 ruby 中这是可能的(比如 python)。
那么模块中包含什么?类、方法、常量。模块在该名称空间下保护它们。
无实例:模块不能用于创建实例
Mixed ins:有时继承模型对类不好,但在功能方面希望将一组类/方法/常量组合在一起
关于 ruby 中模块的规则: - 模块名称是 UpperCamelCase - 模块内的常量全部大写(此规则对所有 ruby 常量都相同,而不是特定于模块) - 访问方法:使用 .运算符 - 访问常量:使用 :: 符号
一个简单的模块示例:
module MySampleModule
CONST1 = "some constant"
def self.method_one(arg1)
arg1 + 2
end
end
如何在模块中使用方法:
puts MySampleModule.method_one(1) # prints: 3
如何使用模块的常量:
puts MySampleModule::CONST1 # prints: some constant
关于模块的其他一些约定:在文件中使用一个模块(如 ruby 类,每个 ruby 文件一个类)
Ruby 中的 Module
在一定程度上对应于 Java 抽象类——具有实例方法,类可以从它继承(通过 include
,Ruby 人称其为“mixin”),但是没有实例。还有其他一些细微的差异,但这么多信息足以让您入门。
底线:模块是静态/实用程序类和 mixin 之间的交叉。
Mixins 是“部分”实现的可重用部分,可以以混合和匹配的方式组合(或组合),以帮助编写新类。当然,这些类还可以有自己的状态和/或代码。
班级
当你定义一个类时,你定义了一个数据类型的蓝图。类保存数据,具有与该数据交互并用于实例化对象的方法。
模块
模块是将方法、类和常量组合在一起的一种方式。
模块给您带来两大好处: => 模块提供命名空间并防止名称冲突。命名空间有助于避免与其他人编写的具有相同名称的函数和类发生冲突。 => 模块实现了 mixin 工具。
(包括 Klazz 中的 Module 使 Klazz 的实例可以访问 Module 方法。)(使用 Mod 扩展 Klazz 使类 Klazz 可以访问 Mods 方法。)
首先,一些尚未提及的相似之处。 Ruby 支持开放类,但模块也是开放的。毕竟,Class 继承自 Class 继承链中的 Module,因此 Class 和 Module 确实有一些相似的行为。
但是您需要问自己在编程语言中同时拥有类和模块的目的是什么?一个类旨在成为创建实例的蓝图,每个实例都是蓝图的实现变体。实例只是蓝图(类)的已实现变体。很自然,类的作用就是创建对象。此外,由于我们有时希望一个蓝图派生自另一个蓝图,因此类旨在支持继承。
模块不能被实例化,不能创建对象,也不支持继承。所以请记住一个模块不会从另一个模块继承!
那么在语言中使用模块有什么意义呢?模块的一个明显用途是创建命名空间,您也会注意到其他语言的这一点。同样,Ruby 的酷之处在于可以重新打开模块(就像类一样)。当您想在不同的 Ruby 文件中重用命名空间时,这是一个很大的用法:
module Apple
def a
puts 'a'
end
end
module Apple
def b
puts 'b'
end
end
class Fruit
include Apple
end
> f = Fruit.new
=> #<Fruit:0x007fe90c527c98>
> f.a
=> a
> f.b
=> b
但是模块之间没有继承:
module Apple
module Green
def green
puts 'green'
end
end
end
class Fruit
include Apple
end
> f = Fruit.new
=> #<Fruit:0x007fe90c462420>
> f.green
NoMethodError: undefined method `green' for #<Fruit:0x007fe90c462420>
Apple 模块没有从 Green 模块继承任何方法,当我们在 Fruit 类中包含 Apple 时,Apple 模块的方法被添加到 Apple 实例的祖先链中,但不是 Green 模块的方法,即使 Green模块是在 Apple 模块中定义的。
那么我们如何获得绿色方法呢?您必须将其明确包含在您的课程中:
class Fruit
include Apple::Green
end
=> Fruit
> f.green
=> green
但是 Ruby 对 Modules 有另一个重要的用途。这是 Mixin 工具,我在另一个关于 SO 的答案中对此进行了描述。但总而言之,mixin 允许您将方法定义到对象的继承链中。通过mixins,可以给对象实例的继承链(include)或者self的singleton_class(extend)添加方法。
iamnotmaynard
已经评论了另一个答案:模块可以将实例方法添加到包含类。