Ruby 的模块
我们在之前的章节中介绍了类,在本章节中,我会来介绍一下 Ruby 模块的概念以及如何去使用一个模块。
1. 什么是模块?
在 Ruby 中,模块在某种程度上类似于类:它们可以持有方法,就像类一样。但是,和类不同的是无法实例化模块,即模块不可以创建对象。因此,与类不同,模块没有new
方法。
那么哪里需要使用模块呢?
使用模块,您可以在类之间共享方法:模块可以包含在类中,这使得它们的方法可以在多个类中使用,就像我们将这些方法复制并粘贴到类定义上一样。这种使用方式我们也称为Mixin
。
2. 模块的使用
2.1 创建模块
创建模块的时候我们会用到关键字module
:
实例:
module Encryption
end
现在我们就创建了一个什么方法都没有的Encryption
模块。
2.2 添加方法
与类中定义实例方法一样,在模块中创建方法只需在模块中定义一个方法即可:
require 'digest'
module Encryption
def encrypt(string)
Digest::SHA2.hexdigest(string)
end
end
现在模块拥有了一个名为encrypt
的方法。这个方法是用于将传入字符串进行 SHA256 加密。
注意事项:因为和类不同,模块没有new
方法,不能实例化成为一个对象,所以不能模块一般通过被类进行引用来执行相应的方法。
Encryption.new
# ---- 输出结果 ----
undefined method `new' for Encryption:Module (NoMethodError)
不过,模块可以通过模块名.方法名()
的形式调用模块方法。
实例:
require 'digest'
module Encryption
def self.encrypt(string) # 注意这里多了一个self
Digest::SHA2.hexdigest(string)
end
end
puts Encryption.encrypt('super password')
# ---- 输出结果 ----
'02f10a4b97a846ae06d64073bb56469d8516bbe19bd1487e9d80ae7e9ec0ac1b'
模块方法的定义形式和类完全一样,因为模块就是类(Class)的父类(Superclass)(在 类的实质 章节中会详细讲解)。
class Person
end
p Person.class.superclass
# ---- 输出结果 ----
Module
2.3 通过引用模块重构代码
让我们用Person
类举例:
require 'digest'
class Person
def initialize(name)
@name = name
end
def name
@name
end
def password=password
@password = password
end
def encrypted_password
Digest::SHA2.hexdigest(@password)
end
end
person = Person.new("Andrew")
person.password = "super password"
p person.encrypted_password
# ---- 输出结果 ----
'02f10a4b97a846ae06d64073bb56469d8516bbe19bd1487e9d80ae7e9ec0ac1b'
Person
类拥有一个对密码加密的方法,让我们对这个方法进行重构。
我们要重构这个获取对密码进行加密之后的结果的方法encrypted_password
。在这时我们选择引用Encryption
模块来添加对字符串加密的encrypt
方法。
实例:
require 'digest'
module Encryption
def encrypt(string)
Digest::SHA2.hexdigest(string)
end
end
class Person
include Encryption
def initialize(name)
@name = name
end
def name
@name
end
def password=password
@password = password
end
def encrypted_password
encrypt(@password)
end
end
person = Person.new("Andrew")
person.password = "super password"
p person.encrypted_password
# ---- 输出结果 ----
'02f10a4b97a846ae06d64073bb56469d8516bbe19bd1487e9d80ae7e9ec0ac1b'
解释: 在这里我们使用了include
关键字来引用模块(引入模块一共有三种方式:include、extend、prepend,在之后的章节中我们会对这三种情况逐个分析),引用的方法都会变成Person
类的实例方法。重构后,我们调用encrypted_password
时加密时使用的encrypt
方法来自Encryption
模块内,这样避免了在很多类中做同一种加密,每修改一次加密形式就要修改每一个类代码的问题。
上述这种情况假设我们还有其它需要加密内容的类,我们还希望将加密的方法保留在一个地方,这么做有 4 个好处:
- 当我们想切换到另一种加密方式的时候,只需要更改这个模块的加密代码即可;
- 我们不希望相同的加密逻辑代码在某些需要的位置重复被使用;
- 可以把这种代码视为一个杂物,隐藏在另一个文件中,我们只需要关心类的工作,不需要关心加密事务的具体逻辑;
- 使用模块来封装代码也会使可读性更高。
3. 小结
本章节中我们学习了模块的概念,区分了类与模块,模块不能创建实例,没有new
方法,以及如何创建一个模块,向模块中添加方法、创建类似类方法的模块方法、引用模块,引用模块封装代码的好处。