## 17.模块和混入
每当你想到模块时,便会想到一个盒子或其他东西。 只需看看你的打印机即可。 它是一个模块。 它可以做一些事情。 它有事要做。 以类似的方式,Ruby 中的模块可以包含 Ruby 代码来执行某些操作。 模块是将 Ruby 代码打包到可能的逻辑单元中的方法。 每当你想在模块中使用代码时,只需将其包含在 Ruby 程序中即可。
让我们看一下第一个名为 [module_function.rb](code/module_function.rb) 的模块程序。 以下程序具有两个模块,即`Star`和`Dollar`。 这两个模块具有称为`line`的相同功能。 请注意,在模块`Star`中的函数`line`中,我们打印一行 20 个星号(*)字符。 在模块`Dollar`中的功能`line`中,我们以类似的方式打印了一行 20 美元($)的字符。 在文本编辑器中键入程序并执行。
```rb
# module_function.rb
module Star
def line
puts '*' * 20
end
end
module Dollar
def line
puts '$' * 20
end
end
include Star
line
include Dollar
line
```
输出量
```rb
********************
$$$$$$$$$$$$$$$$$$$$
```
让我们看一下模块外部的程序。 我们有以下代码:
```rb
include Star
line
include Dollar
line
```
在`include Star`行中,我们包含了`Star`模块中的代码,然后我们调用了函数`line`,因此输出结果是一行 20 星。 看下一行,我们包含模块`Dollar`。 `Dollar`也具有称为`line`的功能。 由于此模块是在`Star`之后调用的,因此 Dollar 模块中的`line`函数会被重写或正确地隐藏`Star`模块中的`line`函数。 因此,在`include Dollar`之后调用`line`将在`Dollar`模块的`line`功能中执行代码。 因此,我们得到一行二十美元的符号。
在下面的示例 [module_function_0.rb](code/module_function_0.rb) 中,我们将看到调用`line`函数而不包含任何模块时发生的情况。 在下面输入程序并执行
```rb
# module_function_0.rb
module Star
def line
puts '*' * 20
end
end
module Dollar
def line
puts '$' * 20
end
end
line
```
Output
```rb
module_function_0.rb:15:in `<main>': undefined local variable or method `line' for main:Object (NameError)
```
如你所见,`line`被视为未定义的局部变量或方法 <sup class="footnote">[ [39](#_footnotedef_39 "View footnote.") ]</sup> 。 因此,可以说只有当模块包含在程序中时,才能访问模块中的功能。
可以说,我们编写了另一个模块,该模块不带任何功能,而只是其中的代码。 我写了以下程序 linclude:code / module.rb [module.rb]只是因为我想看看会发生什么。 事实证明,当模块在 Ruby 文件中编码并执行时,默认情况下将执行模块中的代码。 即使我们不包含该模块,也会发生这种情况。
```rb
# module.rb
module Something
puts "Something"
end
module Nothing
puts "Nothing"
end
```
Output
```rb
Something
Nothing
```
上面程序 [module.rb](code/module.rb) 的输出打印出`Something`和`Nothing`。 我们放置了两个名为`Something`的模块,其中包含代码`puts “Something”`,另一个包含了`Nothing`的模块,其中包含`puts “Nothing”`。 尽管我没有使用`include`语句将这些模块包含在程序中,但无论如何它们下的代码仍会执行。
### 17.1。 不包含的调用函数
在程序 [module_function.rb](code/module_function.rb) 中,我们已经看到了如何包含模块并在其中调用函数。 我们印了一行星星和美元。 让我们以不同的方式做同样的事情。 这次,我们将不再使用 include 关键字。
键入程序 [module_function_1.rb](code/module_function_1.rb) 并执行它。
```rb
# module_function_1.rb
module Star
def Star.line
puts '*' * 20
end
end
module Dollar
def Dollar.line
puts '$' * 20
end
end
Dollar::line
Star::line
Dollar::line
```
Output
```rb
$$$$$$$$$$$$$$$$$$$$
********************
$$$$$$$$$$$$$$$$$$$$
```
看下面的代码:
```rb
Dollar::line
Star::line
Dollar::line
```
当我们调用`Dollar::line`时,将执行`Dollar`模块中的`line`函数。 当我们调用`Star::line`时,将执行`Star`模块中的`line`函数。 因此,当你想在模块中调用函数时,请使用以下语法`<module-name>::<function-name>`。
请注意,在模块`Star`中,我们将功能线定义为`Star.line`,而不仅仅是`line`。 同样,在模块`Dollar`中,我们将其定义为`Dollar.line`。
好,我们开始了解模块,现在让我们开始动手。 键入下面的代码( [module_function_2.rb](code/module_function_2.rb) )并执行它。
```rb
# module_function_2.rb
module Star
def Star.line
puts '*' * 20
end
end
module Dollar
def Dollar.line
puts '$' * 20
end
end
module At
def line
puts '@' * 20
end
end
include At
Dollar::line
Star::line
Dollar::line
line
```
Output
```rb
$$$$$$$$$$$$$$$$$$$$
********************
$$$$$$$$$$$$$$$$$$$$
@@@@@@@@@@@@@@@@@@@@
```
好的,你有一些输出。 看一下以下几行
```rb
include At
Dollar::line
Star::line
Dollar::line
line
```
请注意,我们首先使用`include At`语句包含了`At`模块。 在执行`Dollar::line`语句时,我们得到了一行二十美元的输出。 在执行`Star::line`时,我们得到 20 星的输出。 接下来,我们再次调用`Dollar::line`,然后抓住问题。 我们只调用函数`line`。 由于我们首先包含了`At`,因此当遇到`line`语句时,它将调用`At`模块中的 line 方法。 这表明尽管我们已经调用了`Dollar::line`和`Star::line`,但它并未在程序中包含 <sup class="footnote">[ [40](#_footnotedef_40 "View footnote.") ]</sup> 模块代码,而是仅执行了程序中的特定功能 模块。
在 link:code / module_function _1.rb [module_function _1.rb]中,我们看到了如何在模块中调用函数`Star::line`,其中`Star`是模块名称,`line`是函数名称。 为此,我们在`Star`模块中定义了`line`函数,如下所示
```rb
def Star.line
puts '*' * 20
end
```
我们将其命名为`Star.line`而不是仅将其命名为`line`。 请注意, [module_function_3.rb](code/module_function_3.rb) 与 [module_function_1.rb](code/module_function_1.rb) 相似,但对`Dollar`模块中的`line`功能进行了深入研究。 它没有命名为`Dollar.line`,而是命名为`Star.line`。 如果我们将这样的代码弄乱了怎么办? 执行以下程序并查看。
```rb
# module_function_3.rb
module Star
def Star.line
puts '*' * 20
end
end
module Dollar
def Star.line
puts '$' * 20
end
end
module At
def line
puts '@' * 20
end
end
include At
Dollar::line
Star::line
Dollar::line
line
```
Output
```rb
@@@@@@@@@@@@@@@@@@@@
$$$$$$$$$$$$$$$$$$$$
@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@
```
注意,每当我们调用`Dollar::line`时,都会调用`At`模块中的`line`函数。 这是因为由于我们在`Dollar`模块中将其定义为`Star.line`,所以`Dollar::line`不存在,因此在`At`模块中调用了功能`line`。 注意,我们已经使用语句 include `At`包含了`At`模块。
现在我们考虑另一种情况,在`Dollar`模块中(参见程序 [module_function_4.rb](code/module_function_4.rb) ),我们仅将函数`line`定义为`line`而不是`Dollar.line`。 在下面的程序中使用`Dollar::line`调用它时,我们看到`At`模块中的`line`函数被调用。 因此,故事的寓意是,如果要在程序中调用`<module-name>::<function-name>`,请确保该函数在模块内的名称为`<module-name>.<function-name>`。
```rb
# module_function_4.rb
module Star
def Star.line
puts '*' * 20
end
end
module Dollar
def line
puts '$' * 20
end
end
module At
def line
puts '@' * 20
end
end
include At
Dollar::line
Star::line
Dollar::line
line
```
Output
```rb
@@@@@@@@@@@@@@@@@@@@
********************
@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@
```
### 17.2。 模块中的类
我们已经了解了 Ruby 解释器如何对模块中的函数进行操作。 现在让我们看一下它对模块中类的行为。 在 [module_class.rb](code/module_class.rb) 下键入程序并执行。
```rb
# module_class.rb
module Instrument
class Ruler
def what_u_do?
puts "I take measurements"
end
end
end
module People
class Ruler
def what_u_do?
puts "I govern this land"
end
end
end
r1 = People::Ruler.new
r2 = Instrument::Ruler.new
r1.what_u_do?
r2.what_u_do?
```
Output
```rb
I govern this land
I take measurements
```
让我们分析程序。 我们有两个模块。 第一个名为`Instrument`,第二个名为`People`。 两者都有一个名为`Ruler`的类,并且两个 Ruler 都有一个名为`what_u_do?`的方法。 很好,现在进入程序。 我们有一条语句`r1 = People::Ruler.new`,其中`r1`成为`People`模块中`Ruler`类的实例变量。 同样,我们具有`r2 = Instrument::Ruler.new`,其中`r2`成为`Instrument`模块中`Ruler`类的实例变量。
可以通过执行以下代码来验证
```rb
r1.what_u_do?
r2.what_u_do?
```
其中调用`r1.what_u_do?``输出`I govern this land`,调用`r2.what_u_do?`输出`I take measurements`。
这个故事的寓意是,你可以在不同的模块中使用相同的类名称,只需使用`<module-name>::<class-name>`即可调用该类。 。
### 17.3。 混合蛋白
模块的另一种用途是你可以根据需要在模块中混合代码。 这个叫做 mixin。 我们已经了解了 mixin,但我没有告诉你这是 mixin。 例如,假设你正在用 Ruby for Linux 和 Apple 机器编写某些应用程序。 你会发现某些代码仅在 Linux 上有效,而其他代码仅在 Apple 上有效,然后可以如下所示将它们分开。 Apple 组件进入 Apple 模块,Linux 组件进入 Linux 模块。
假设你的朋友使用 Linux 机器并想要运行你的程序。 你需要做的就是在代码中包含 Linux,如下所示。
```rb
# mixin.rb
module Linux
# Code for linux goes here
def function
puts "This function contains code for Linux systems"
end
end
module Apple
# Code for apple goes here
def function
puts "This function contains code for Apple systems"
end
end
include Linux
function
```
Output
```rb
This function contains code for Linux systems
```
调用方法`function`时,将调用`Linux`模块中的方法`function`。 简而言之,你已经在程序中混入了 Linux 代码,并保留了 Apple 的东西。
让我们看看 mixin 的另一个例子。 看看下面的代码 [mixin_2.rb](code/mixin_2.rb) 。 可以说你的客户告诉他,他非常需要一个程序来计算圆的面积和球的体积。 因此,你开发了两个名为`Circle`和`Sphere`的类,并配有代码来查找面积和体积。 所以你的客户很高兴。 由于你的客户位于银河星系中,因此常数 Pi <sup class="footnote">[ [41](#_footnotedef_41 "View footnote.") ]</sup> 为 22 除以 7。因此,我们将`Pi`的值放在名为`Constants`的模块中,然后 使用语句`include Constants`包含在`Circle`和`Sphere`类中。 在下面的程序中键入并执行它。
```rb
# mixin_2.rb
module Constants
Pi = 22.0/7
end
class Circle
include Constants
attr_accessor :radius
def area
Pi * radius * radius
end
end
class Sphere
include Constants
attr_accessor :radius
def volume
(4.0/3) * Pi * radius ** 3
end
end
c = Circle.new
c.radius = 7
s = Sphere.new
s.radius = 7
puts "Circle Area = #{c.area}"
puts "Sphere Volume = #{s.volume}"
```
Output
```rb
Circle Area = 154.0
Sphere Volume = 1437.333333333333
```
这样你就可以得到一些输出。 你可能会问,将常量放入模块并使用`include`语句将其混合在类中有何好处? 上面的程序教你两种道德
* 你可以将常量放入模块中
* 如果你具有可以在类之间共享的通用代码 <sup class="footnote">[ [42](#_footnotedef_42 "View footnote.") ]</sup> ,则可以将其放入模块中并进行共享。
如果你在每个类中分别定义了`Pi`的值,并且你碰巧从仙女座星系中获得了 Pi 为 57 除以 18.1364 的客户端,则可以只在一个地方进行更改,即在`Constants`模块和 看到更改反映在许多类中(在我们的例子中是`Circle`和`Sphere`类)。
因此,道德模块有助于我们迎合超出我们银河系的客户,并且我们可以真正建立银河帝国 <sup class="footnote">[ [43](#_footnotedef_43 "View footnote.") ]</sup> 。
- 前言
- 红宝石
- 先决条件
- 1.安装 Ruby
- 2.在线资源
- 3.入门
- 4.比较与逻辑
- 5.循环
- 6.数组
- 7.哈希和符号
- 8.范围
- 9.功能
- 10.可变范围
- 11.类&对象
- 12.安全导航
- 13.打破大型程序
- 14.结构和 OpenStruct
- 15. Rdoc
- 16. Ruby 样式指南
- 17.模块和混入
- 18.日期和时间
- 19.文件
- 20. Proc,Lambda 和块
- 21.多线程
- 22.异常处理
- 23.正则表达式
- 24.宝石
- 25.元编程
- 26.基准
- 27.测试驱动开发
- 28.观察者模式
- 29.模板模式
- 30.工厂模式
- 31.装饰图案
- 32.适配器模式
- 33.单例模式
- 34.复合模式
- 35.建造者模式
- 36.策略模式
- 赞助商
- 捐
- 人们怎么说
- 版权
- 取得这本书