`
yanzilee9292
  • 浏览: 538482 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Module的使用方法

    博客分类:
  • ruby
阅读更多

模块提供了一种组织常量,类和方法的手段。你可以使用模块来提供一个名字空间以避免名字冲突,你也可以使用模块来提供 mixin 的功能。

名字空间

当程序代码越来越多,工程越来越大,开发者不可避免的会将一些常用的代码以库或别的形式重用。一般情况下,我们可以用类来组织代码,但有时候使用类组织代码并不是十分合适。这样在一个大工程中,就有可能发生名字冲突。

 

例如,开发者 A 在文件 a.rb 中写了如下代码,用来输出自己代码文件的版本信息,

           def print_version

       # …

       end

 

同一个项目中的另一个开发者 B 在文件 b.rb 中用同样的方法来实现同样的功能,

           def print_version

       # …

       end

 

第三个开发者 C 需要使用 a.rb 和 b.rb 中的一些方法,这样,当他使用 print_version 方法时,就产生了名字冲突,到底他调用的是哪一个文件中的 print_version 方法呢?

 

我们可以使用模块机制来解决这样的名字冲突。定义一个模块相当于定义了一个名字空间,名字空间内的元素在全局空间并不直接可见。

 

开发者 A 定义模块 A_FILE ,

       module A_FILE

           def print_version

           # …

           end

       end

开发者 B 定义模块 A_FILE ,

       module B_FILE

           def print_version

           # …

           end

       end

这样对于开发者 C ,可以这样使用 print_version

       require ‘A’

       require ‘B’

A.  print_version

B.  print_version

 

类和模块的区别是,模块不能生成实例,而类不能被 include 。

Mix-in 的意思是混合插入、糅合,就像在冰淇淋中混合多种配料可以做成美味的混合冰淇淋一样,在类中混合插入各种模块就可以添加相应的功能。模块还有另一个重要的作用,可以使用模块来实现多继承,可以在类中包含模块,这样模块中的所有方法和类中其他方法一样可以使用。

Matz 坚信滥用多重继承会导致继承关系的混乱,因此 Ruby 中不允许使用多重继承。同时为充分发挥继承功能的优势, Ruby 支持两种继承关系: 1. 使用 is-a 语句的继承; 2. 使用 Mix-in 来共享并继承模块中的功能。

 

    module Debug

       Define print_info

           print "Class: #{self.class.name} Object ID: #{self.id}"

       end

    end

 

    class A

       include Debug

       #...

    end

 

    class B

       include Debug

       #...

    end

 

    obj1 = A.new

    obj2 = B.new

    obj1.print_info

    obj2.print_info

 

通过这样的手段我们可以实现多继承的功能, 这样的模块我们称为 mixin 。

 

在Ruby 中,Object ,Class 和Module 是三个特殊的类。

Class 是一个Module ,而Module 是一个Object ,所以说Class 是一个Object ,因此,所有的数据都是Object 。

 

  使用 mixin

   Comparable

Comparable mixin 提供了比较的功能。要使用 Comparable mixin 必须提供 <=> 方法, <=> 的返回值为 -1 , 0 , +1 用来表示元素之间的小于,等于,大于的关系。

class Person

  include Comparable

  attr :age

  def <=>(aPerson)

     @age <=> aPerson.age

  end

  def initialize(name, gender, age)

     @name = name

     @gender = gender

     @age = age

  end

end

 

aPerson = Person.new("Tom", "male", 18)

bPerson = Person.new("Mike", "male", 10)

cPerson = Person.new("Henry", "male", 40)

puts aPerson > bPerson

puts aPerson < bPerson

puts aPerson >= bPerson

puts aPerson <= bPerson

puts aPerson == bPerson

puts aPerson.between?(bPerson, cPerson)

 

执行结果为 :

true

false

true

false

false

true

 

   Enumerable

Enumerable mixin 提供了遍历,查找和排序的功能。 要使用 Enumerable mixin 必须提供 each 方法,标准做法是在 each 方法内对每一个元素使用 yield 操作。如果使用了 Enumerable mixin 中的 max , min ,或 sort ,那么还必须提供 <=> 方法,用来实现元素之间的比较关系。

 

以下是一个使用 Enumerable mixin 的例子, IntegerFinder 是一个查找字符串中整数的类。

class IntegerFinder

       include Enumerable

 

        def initialize(aString)

           @string = aString

       end

 

def each

           @string.scan(/[1-9]\d*/) { |integer| yield integer }

       end

end

 

aDigitFinder = IntegerFinder.new("This is 123, 234, 98 and 10")

aDigitFinder.collect {|i| print i, " "}

aArray = aDigitFinder.find_all {|i|  i.to_i > 50 }

puts "\n", aArray.to_s

 

执行结果为:

    123 234 98 10

12323498

 

Enumerable mixin 中含有许多与集合遍历查找相关的方法,许多标准类也使用了 Enumerable mixin ,借助 Enumerable mixin 中的方法可以方便的实现一些强大的功能,请看以下一些例子:

 

# 察看数组中的所有单词的长度是否大于 4

%w{ ant bear cat}.all? {|word| word.length >= 4}   #=> false

 

    # 返回 range 中所有不符合条件的元素

(1..10).reject {|i|  i % 3 == 0 } #=> [1, 2, 4, 5, 7, 8, 10]

 

# 求 5 到 10 的和

#inject 方法第一次会把 Range 中的前两个元素作为参数传递给 sum 和 n ,

# 以后每次会把 sum 设置为块计算后的返回值。

(5..10).inject {|sum, n| sum + n }                    #=> 45

 

# 找出数组中最长的单词

longest = %w{ cat sheep bear }.inject do |memo,word|

    memo.length > word.length ? memo : word

end

longest                                         #=> "sheep"

 

  Singleton

在设计模式中, Singleton 技术通常称为单件,是一种常见的设计技术,它保证在系统的某个类在任一时刻最多只有一个实例在运行。你可以参见设计模式这本书获得有关单件技术更详细的信息。在 Ruby 中,使用 Singleton Mix-in ,你可以很容易的实现单件类。

 

       在单件类中不能使用 new 方法,因为在单件类中 new 方法的属性是私有的。需要使用 instance 方法得到类的实例对象。

 

require 'singleton'

 

class SingletonClassTest

        attr_accessor :data

        include Singleton

end

 

# a = SingletonClassTest.new # 错误, new 方法为私有方法

a = SingletonClassTest.instance

b = SingletonClassTest.instance

 

puts a.inspect

puts b.inspect

 

a.data = 8

puts b.data

 

执行结果为:

#<SingletonClassTest:0x2ab9360>

#<SingletonClassTest:0x2ab9360>

8

 

可以看到, a 和 b 其实指向同一个对象。

 

  Require, load 和 include

在 Ruby 中,可以使用 load 和 require 来包含另一个文件。每次运行到 load 时, load 后的文件会被载入并执行。

4.times do |i|

        File.open("temp.rb","w") do |f|

           f.puts "def test"

           f.puts "#{i}"

            f.puts "end"

        end

        load "temp.rb"

        puts test

end

执行结果为:

0

1

2

3

在上面的小程序里 load "temp.rb" 执行了 4 次,每一次 temp.rb 文件都不同,所以 test 方法执行后的结果也不同。

使用 Load 方法的这种特性可以实现一些强大的功能,例如:

l  可以用来处理配置文件,在程序运行期间配置文件可以被动态改变。

l  可以用来实现程序的无缝升级,在升级时你不需要重启程序,只要将所需要的代码重新 load 。

 

Require 和 load 不同,它只加载文件一次,即在第一次执行到 require 时载入,以后碰到 require 同一文件时自动忽略。已经被加载的文件保存在 $” 中。另外, require 还可以用来加载二进制格式的库。 Require 可以使用绝对路径或相对路径,如果使用了相对路径,那么系统会在 $: 变量包含的目录中搜寻。 Require 和 load 的另一个不同是当包含文件是 Ruby 代码时, require 可以不加后缀名。 Require 将当前所有加载的文件保存在 $" 变量中。

 

注意,在当前版本中, $” 是一个数组,保存着使用 require 已经加载过的文件。但如果 require 使用不同的路径去包含同一个文件,这个文件就有可能被加载多次。

    File.open("temp.rb","w") do |f|

       f.puts "def test"

       f.puts "1"

       f.puts "end"

end

require "temp"

puts test

 

File.open("temp.rb","w") do |f|

       f.puts "def test"

       f.puts "2"

       f.puts "end"

end

 

require "./temp"

puts test

 

执行结果为:

1

2

 

这样就违背了 require 只加载一次的初衷,一些人认为这是一个 bug ,这个问题在 Ruby 的后续版本中可能被修复。所以,不要使用不同的路径去加载同一个文件。

 

require, load,include 都是 Kernel 模块中的方法,他们的区别如下:

l  require , load 用于包含文件, include 则用于包含的模块。

l  require 加载一次, load 可加载多次。

l  require 加载 Ruby 代码文件时可以不加后缀名, load 加载代码文件时必须加后缀名。

l  require 一般情况下用于加载库文件,而 load 用于加载配置文件。

分享到:
评论

相关推荐

    vue 之 css module的使用方法

    最近学习webpack看到了一个新鲜的东西,之前都是通过scoped来区别类名,秉着任何时候学习都不晚的心情,作为小白的我也想揭揭css module的神秘面纱。 css module目的为所有类名重新生成类名,有效避开了css权重和...

    Android Studio 多层级 Module 对 aar 引用问题解决方法

    问题:有个arr文件被放到Module A中引用,现在Module B又依赖了Module A,则在编译过程中会发生错误,Module B找不到aar文件。(同时如果又有Module C 依赖了Module B,C也会出同样的问题) 解法: 1、正常给一个...

    android studio删除module方法

    在Android Studio中,删除一个...此外,如果你使用的是其他版本控制系统(如Git、SVN等),操作流程可能会有所不同,但基本思路是一致的,即先从项目配置中移除Module,然后从文件系统和版本控制库中删除相关文件。

    运行python提示no module named sklearn的解决方法

    使用pip包管理器安装包的方法如下: 在命令行中输入:pip install sklean 如果成功安装,会提示“Successfully installed sklean”。 其实参考下面的方法 1.安装支持部分: 在terminal里面直接输入以下命令,这个...

    node.js module

    7. **`exports` 与 `module.exports` 的区别**:虽然通常情况下两者可以互换使用,但当需要导出一个完整的对象或函数时,应优先使用 `module.exports`。因为如果 `exports` 被赋值为一个新对象,原来的 `module....

    jackson-module-jaxb-annotations-2.7.8-API文档-中英对照版.zip

    赠送jar包:jackson-module-jaxb-annotations-2.7.8.jar; 赠送原API文档:jackson-module-jaxb-annotations-...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,

    powermock-module-junit4-2.0.9-API文档-中英对照版.zip

    使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。 双语对照,边学技术、边学英语。

    android之APP抽离Module及自动化移动全部资源

    为了自动化移动资源,你可以编写Gradle脚本或使用第三方工具,例如使用Android Studio的Refactor &gt; Move功能,或者利用像Retrofit、Butter Knife这样的依赖注入框架,它们可以帮助识别并自动迁移相关资源。...

    python提示No module named images的解决方法

    本文讲述了python提示No module named images的解决方法,非常实用!分享给大家供大家参考。具体方法如下: 出现提示:ImportError: No module named images 表示找不到images模块 可将: import images 替换为:...

    jackson-module-paranamer-2.7.9-API文档-中文版.zip

    赠送jar包:jackson-module-paranamer-2.7.9.jar...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。

    jackson-module-scala_2.11-2.6.7.1-API文档-中英对照版.zip

    赠送jar包:jackson-module-scala_2.11-2.6.7.1.jar;...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请

    intellij idea 设置多module路径.docx

    在IntelliJ IDEA中,开发大型复杂项目时,通常会使用多模块(module)结构来组织代码。然而,在这样的环境中,我们可能会遇到一个问题,即在运行或调试代码时,`System.getProperty("user.dir")`方法返回的不是当前...

    前端开源库-json2module

    本文将深入探讨这个库的核心原理、使用方法以及它在实际项目中的应用场景。 ### JSON2Module简介 `json2module` 是一个针对前端开发的轻量级库,它的主要功能是将JSON格式的数据转换为符合ES6模块规范的JavaScript...

    flex一个动态添加卸载Module的例子

    - 使用事件来协调Module之间的通信,避免直接引用。 - 考虑到错误处理,添加适当的异常捕获和用户反馈机制。 综上所述,本示例展示了如何在Flex环境中利用ModuleLoader动态加载和卸载Module,以实现更加灵活和...

    perl module 下载列表

    3. **查看文档**:找到合适的模块后,阅读其README文件或 POD (Plain Old Documentation) 文档,了解模块的功能、使用方法和依赖项。 4. **测试与安装**:在正式使用前,你可能需要先在本地环境中测试模块。可以使用...

    jackson-module-jaxb-annotations-2.7.8-API文档-中文版.zip

    赠送jar包:jackson-module-jaxb-annotations-2.7.8.jar; 赠送原API文档:jackson-module-jaxb-...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结

    jackson-module-jaxb-annotations-2.2.3-API文档-中文版.zip

    赠送jar包:jackson-module-jaxb-annotations-2.2.3.jar; 赠送原API文档:jackson-module-jaxb-...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结

    modulejs轻量级的JavaScript模块化系统

    这个模块导出了一个名为`sayHello`的方法,其他模块可以使用`require`来加载并使用它。 **模块加载** 加载模块通常使用`require`函数,如下所示: ```javascript // main.js var myModule = require('./myModule'...

    ngx_http_proxy_connect_module.zip

    ngx_http_proxy_connect_module是一个针对Nginx服务器的第三方模块,主要功能是支持HTTP代理的"CONNECT"方法。在默认情况下,Nginx仅处理HTTP和HTTPS请求,但不支持通过HTTP代理进行TCP连接,比如SSL/TLS隧道。这个...

    报错:Uncaught SyntaxError: Cannot use import statement outside a module 详解

    JS错误Uncaught SyntaxError: ...在报错中了解到,是说无法在模块外部使用import语句,因为Module 的加载实现的是es6语法,所以在浏览器加载html文件时,需要在script 标签中加入type=”module”属性。 解决办法:

Global site tag (gtag.js) - Google Analytics