在类方法中使用实例变量 - Ruby

72

我有一个类似以下的类,并且我使用实例变量(数组)来避免使用许多方法参数。

它按照我的预期工作,但这是一个好的做法吗? 实际上,我不会预期它可以工作,但我猜测类方法并不像其他语言中的静态方法那样工作。

class DummyClass
  def self.dummy_method1
    @arr = []
    # Play with that array
  end

  def self.dummy_method2
    # use @arr for something else
  end
end
2个回答

95
Ruby中实例变量在类上工作的原因是Ruby类本身就是实例(Class类的实例)。通过检查DummyClass.class来自己试一下。在Ruby中没有C#意义上的“静态方法”,因为每个方法都定义在(或继承到)某个实例上,并在某个实例上调用。因此,它们可以访问调用者上可用的任何实例变量。
由于DummyClass是一个实例,所以它可以有自己的实例变量。只要您有对类的引用(应该始终如此,因为类名是常量),甚至可以访问那些实例变量。在任何时候,您都可以调用::DummyClass.instance_variable_get(:@arr)并获取该实例变量的当前值。
至于是否这样做是好事,这取决于方法
如果@arr在逻辑上是实例/类DummyClass的“状态”,则将其存储在实例变量中。如果@arr仅在dummy_method2中用作操作快捷方式,则将其作为参数传递。举个使用实例变量方法的例子,考虑Rails中的ActiveRecord。它允许您这样做:
u = User.new
u.name = "foobar"
u.save

在这里,分配给用户的名称是合法地存在于用户上的数据。如果在调用#save之前问“此时用户的名称是什么”,你会回答“foobar”。如果你深入到内部(你需要很深入并进行大量元编程),你会发现它们确实使用实例变量来做到这一点。
我使用的示例包含两个单独的公共调用。要查看一个即使只进行了一个调用也仍然使用实例变量的情况,请查看ActiveRecord实现的#update_attributes。该方法体简单地为load(attributes, false) && save。为什么#save没有传递任何参数(比如新的name),即使它将在保存的主体中出现,例如UPDATE users SET name='foobar' WHERE id=1;? 这是因为像名称这样的信息属于实例。
相反,我们可以看一个例子,其中实例变量没有使用意义。看看#link_to_if的实现,这个方法接受一个布尔值参数(通常是源代码中的表达式),以及#link_to通常接受的参数,例如链接到的URL。当布尔条件为真时,它需要将其余的参数传递给#link_to并调用它。在这里分配实例变量并没有太多意义,因为您不会说调用上下文(渲染器)在实例中包含该信息。渲染器本身没有“链接到的URL”,因此不应该被埋藏在实例变量中。

2
感谢您提供这个非常好的解释和例子,特别是关于是否是一个好的实践。 - tackleberry

32

这些是类实例变量,在 Ruby 中是完全合法的事情:类也是对象(Class 的实例),因此具有实例变量。

需要注意的一件事是,每个子类都会有自己的类实例变量(毕竟它们是不同的对象):如果你从 DummyClass 创建了一个子类,子类上的类方法将无法看到 @arr

当然,类变量(@@foo)则相反:整个类层次结构共享相同的类变量。


嗯,那是有些不同的。只要我不继承一个新类,那就没什么好担心的了。这是个好消息,谢谢! - tackleberry
感谢您的澄清!我已经使用Ruby多年了,但现在才遇到类实例变量。这对于理解正在发生的事情非常有帮助。 - Topher Fangio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接