我正在制作一个具有一些简单功能的实用程序模块 - 我希望该模块能够扩展自身,因此不需要实例化。我和一位同事正在讨论在这些类型的模块中存储数据的各种方法,他提供的一个例子是使用实例变量:
module ExampleModule
extend self
@my_instance_variable = "Hello from module"
def do_thing
puts "**** #{@my_instance_variable}"
@my_instance_variable
end
end
这不是我们决定使用的方法,但我想更好地了解它为何有效/最佳替代方案。那么,有几个问题:
为什么这有效?在没有实例的情况下调用的模块方法如何访问实例变量?您能给我指出任何讨论此行为的 ruby 文档/文章吗?
思考这是否/何时是合适的模式? (我不是粉丝,因为像这样的模块拥有实例变量似乎违反直觉)
这里应该使用类变量吗?我们的 rubocop 将 @@foo 类变量标记为代码味道
Ruby 中的类和模块都可以有实例变量。那是因为它们分别是类和模块的实例。如果您使用 Module.new 方法而不是关键字,这会变得更加明显:
module Foo
@bar = "Hello World"
def self.bar
@bar
end
end
# is pretty much equivilent to
Foo = Module.new do
@bar = "Hello World"
def self.bar
@bar
end
end
上面的示例和您的代码之间唯一真正的区别是您使用的是
extend self
- 这导致 do_thing
在模块上可调用,而不仅仅是由模块扩展的类。
我不是这个的忠实粉丝,恕我直言,最好使用
def self.method_name
显式定义方法,这样可以更清楚地了解意图,并且可以通过静态分析来获取。
思考这是否/何时是合适的模式? (我不是粉丝,因为像这样的模块拥有实例变量似乎违反直觉)
模块实例变量的一个非常常见的用途配置模式:
module MyGem
class << self
attr_accessor :configuration
end
def self.configure
self.configuration ||= Configuration.new
yield(configuration)
end
class Configuration
attr_accessor :mailer_sender
def initialize
@mailer_sender = '[email protected]'
end
end
end
MyGem.configure do |config|
config.mailer_sender = '[email protected]'
end
如果您是 Ruby 新手,这可能会令人困惑,但请记住,在 Ruby 中,一切都是对象,并且实例变量不是类的已定义属性。它们只是词法作用域到某事物的实例的变量,可以是模块、类或类的实例。