ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## 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`被视为未定义的局部变量或方法 &lt;sup class="footnote"&gt;[ [39](#_footnotedef_39 "View footnote.") ]&lt;/sup&gt; 。 因此,可以说只有当模块包含在程序中时,才能访问模块中的功能。 可以说,我们编写了另一个模块,该模块不带任何功能,而只是其中的代码。 我写了以下程序 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`函数。 因此,当你想在模块中调用函数时,请使用以下语法`&lt;module-name&gt;::&lt;function-name&gt;`。 请注意,在模块`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`,但它并未在程序中包含 &lt;sup class="footnote"&gt;[ [40](#_footnotedef_40 "View footnote.") ]&lt;/sup&gt; 模块代码,而是仅执行了程序中的特定功能 模块。 在 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`函数被调用。 因此,故事的寓意是,如果要在程序中调用`&lt;module-name&gt;::&lt;function-name&gt;`,请确保该函数在模块内的名称为`&lt;module-name&gt;.&lt;function-name&gt;`。 ```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`。 这个故事的寓意是,你可以在不同的模块中使用相同的类名称,只需使用`&lt;module-name&gt;::&lt;class-name&gt;`即可调用该类。 。 ### 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 &lt;sup class="footnote"&gt;[ [41](#_footnotedef_41 "View footnote.") ]&lt;/sup&gt; 为 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`语句将其混合在类中有何好处? 上面的程序教你两种道德 * 你可以将常量放入模块中 * 如果你具有可以在类之间共享的通用代码 &lt;sup class="footnote"&gt;[ [42](#_footnotedef_42 "View footnote.") ]&lt;/sup&gt; ,则可以将其放入模块中并进行共享。 如果你在每个类中分别定义了`Pi`的值,并且你碰巧从仙女座星系中获得了 Pi 为 57 除以 18.1364 的客户端,则可以只在一个地方进行更改,即在`Constants`模块和 看到更改反映在许多类中(在我们的例子中是`Circle`和`Sphere`类)。 因此,道德模块有助于我们迎合超出我们银河系的客户,并且我们可以真正建立银河帝国 &lt;sup class="footnote"&gt;[ [43](#_footnotedef_43 "View footnote.") ]&lt;/sup&gt; 。