Ruby 模块
Ruby 模块:命名空间和 Mixins (混入)
Section titled “Ruby 模块:命名空间和 Mixins (混入)”Ruby 中的模块主要有两个用途:
- **命名空间(Namespacing):**它们提供了一种方式来对相关的方法、类和常量进行分组,防止代码库的不同部分或与第三方库之间发生名称冲突。
- **Mixins(混入):**它们允许在多个类之间共享通用功能(实例方法),而无需使用传统的继承,提供了一种多重继承的形式。
定义模块的语法:
Section titled “定义模块的语法:”模块使用 module 关键字定义,后跟模块名(按照约定,模块名应使用 CamelCase),以 end 结束。
module MyLogger PI = 3.14159 # A constant within the module
# A module method (called on the module itself) def self.log(message) puts "[LOG] #{Time.now}: #{message}" end
# An instance method (intended for mixing into classes) def print_info(text) "INFO: #{text}" endend访问模块常量和方法:
模块常量使用 ::(作用域解析)运算符访问:MyLogger::PI。
模块方法(使用 self. 或 ModuleName. 定义)直接在模块上调用:MyLogger.log("Application started")。
Ruby require 和 require_relative 语句:
Section titled “Ruby require 和 require_relative 语句:”要使用定义在单独文件中的模块,你需要加载该文件。Ruby 提供了两种主要方式来实现:
require 'filename': 加载一个文件。Ruby 会在$LOAD_PATH(一个包含路径的数组)列出的目录中搜索该文件。它通常用于加载已安装的 Gem 或标准库功能。通常可以省略.rb扩展名。require_relative 'filename': 加载相对于包含require_relative语句的文件路径的文件。这更适用于加载你自己的项目文件。通常包含.rb扩展名,或者如果很明确也可以省略。
命名空间示例:
Section titled “命名空间示例:”假设有两个不同的库都定义了一个 Utility 模块。
module Payments module Utility def self.format_currency(amount) "$%.2f" % amount end endend
# File: reporting_utility.rbmodule Reporting module Utility def self.generate_report_name(base_name) "#{base_name}_#{Time.now.strftime('%Y%m%d')}.pdf" end endend
# File: main_app.rbrequire_relative 'payments_utility'require_relative 'reporting_utility'
puts Payments::Utility.format_currency(123.45) # Output: $123.45puts Reporting::Utility.generate_report_name("sales") # Output: sales_20230315.pdf (date will vary)这演示了模块如何防止名称冲突。Payments::Utility 与 Reporting::Utility 是不同的。
Ruby include 语句:Mixins (混入)
Section titled “Ruby include 语句:Mixins (混入)”include 关键字用于将模块的实例方法和常量混入(mixin)到类中。当一个类 include 一个模块时,该模块的实例方法就会成为这个类的实例方法。
include 用于 Mixins 的示例:
module Printable # This instance method will be mixed into any class that includes Printable def pretty_print "<#{self.class} - #{self.object_id}> Attributes: #{instance_variables.map { |var| "#{var}=#{instance_variable_get(var).inspect}" }.join(', ')}" endend
# File: book.rbrequire_relative 'printable'
class Book include Printable # Mixes in the pretty_print method from Printable module
attr_accessor :title, :author
def initialize(title, author) @title = title @author = author endend
class Magazine include Printable attr_accessor :name, :issue_date
def initialize(name, issue_date) @name = name @issue_date = issue_date endend
# File: app.rbrequire_relative 'book'require_relative 'magazine'
my_book = Book.new("The Great Gatsby", "F. Scott Fitzgerald")my_magazine = Magazine.new("Tech Weekly", "2023-10-26")
puts my_book.pretty_print# Example Output: <Book - 701...> Attributes: @title="The Great Gatsby", @author="F. Scott Fitzgerald"
puts my_magazine.pretty_print# Example Output: <Magazine - 702...> Attributes: @name="Tech Weekly", @issue_date="2023-10-26"在这个示例中,Book 和 Magazine 类都获得了 pretty_print 功能,而无需为了这个特定行为而继承一个共同的超类。这就是 Mixins 的强大之处。
被 include 的模块中的常量也可以直接在类中访问(例如,如果 Printable 有一个常量 VERSION,Book 可以通过 VERSION 访问它)。
Ruby extend 语句:添加类方法
Section titled “Ruby extend 语句:添加类方法”include 将模块的方法添加为实例方法,而 extend 将它们添加为类的类方法。
module ClassUtils def find(id) # Hypothetical find logic for a class method puts "#{self.name} finding record with id: #{id}" # new(id: id) # for example endend
# File: user.rbrequire_relative 'class_utils'
class User extend ClassUtils # Adds ClassUtils methods as class methods to User
attr_accessor :id, :name def initialize(id: nil, name: nil) @id = id @name = name endend
# File: app.rbrequire_relative 'user'
User.find(101) # Calls the 'find' method as a class method on User# Output: User finding record with id: 101Ruby prepend 语句(高级):
Section titled “Ruby prepend 语句(高级):”prepend 在 Ruby 2.0 中引入,类似于 include,但它将模块插入到继承链中,位于类本身之前。这意味着被 prepend 的模块中的方法可以覆盖类中的方法,并且可以使用 super 调用类原有的方法。这是一个更高级的特性,常用于方法装饰或以比 include 更深入的方式修改行为。
module LoggingPrepender def save puts "[PREPEND LOG] Before saving #{self.class.name}..." result = super # Calls the original 'save' method of the class puts "[PREPEND LOG] After saving #{self.class.name}... Result: #{result}" result endend
class Document prepend LoggingPrepender
def save puts "Document saving..." true # Simulate successful save endend
doc = Document.newdoc.save# Output:# [PREPEND LOG] Before saving Document...# Document saving...# [PREPEND LOG] After saving Document... Result: true实际应用:
模块在 Ruby 中被广泛使用。整个 Ruby 标准库都组织在模块中(例如 Math, File, Enumerable)。Rails 框架组件如 ActiveRecord::Base 或 ActionController::Base 包含了许多模块来提供其丰富的功能集。你会使用模块来组织自己的代码、共享工具函数以及实现横切关注点(cross-cutting concerns)如日志或序列化。