在类方法中使用实例变量 - Ruby
我有一个类似下面的类,我使用实例变量(数组)来避免使用大量方法参数。
它按我的预期工作,但这是一个好的做法吗? 实际上我不希望这有效,但我猜类方法在其他语言中不能像静态方法一样工作。
class DummyClass
def self.dummy_method1
@arr = []
# Play with that array
end
def self.dummy_method2
# use @arr for something else
end
end
I have a class something like below, and I used instance variables (array) to avoid using lots of method parameters.
It works as I expected but is that a good practice?
Actually I wouldn't expect that worked, but I guess class methods are not working as static methods in other languages.
class DummyClass
def self.dummy_method1
@arr = []
# Play with that array
end
def self.dummy_method2
# use @arr for something else
end
end
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
实例变量作用于 Ruby 中的类的原因是 Ruby 类本身就是实例(类的实例 类)。通过检查
DummyClass.class
亲自尝试一下。 Ruby 中不存在 C# 意义上的“静态方法”,因为每个方法都是在某个实例上定义(或继承到)并在某个实例上调用的。因此,他们可以访问被调用方可用的任何实例变量。由于
DummyClass
是一个实例,因此它可以有自己的实例变量。只要您有对类的引用(这应该始终是因为类名是常量),您甚至可以访问这些实例变量。在任何时候,您都可以调用::DummyClass.instance_variable_get(:@arr)
并获取该实例变量的当前值。至于好不好做,要看方法。
如果@arr在逻辑上是实例/类DummyClass的“状态”,则将其存储在实例变量中。如果
@arr
仅在dummy_method2
中用作操作快捷方式,则将其作为参数传递。为了给出使用实例变量方法的示例,请考虑 Rails 中的 ActiveRecord。它允许您执行以下操作:在这里,分配给用户的名称是该用户的合法数据。如果在
#save
调用之前询问“此时用户的名字是什么”,您会回答“foobar”。如果您深入研究内部结构(您将深入研究大量元编程,您会发现它们正是为此使用实例变量)。我使用的示例包含两个单独的公共调用。要查看尽管只进行了一次调用但仍然使用实例变量的情况,请查看
#update_attributes
。方法主体很简单load(attributes, false) &&保存。为什么
#save
没有传递任何参数(例如新的name
),即使它将位于 save 正文中,其中诸如UPDATE users SET 之类的内容name='foobar' WHERE id=1;
?这是因为像名称这样的东西是属于实例的信息。相反,我们可以看看实例变量没有任何使用意义的情况。查看
#link_to_if
的实现,一种接受布尔型参数的方法(通常是源代码中的表达式)以及通常被#link_to
接受的参数,例如要链接到的 URL。当布尔条件为真时,它需要将其余参数传递给#link_to并调用它。在这里分配实例变量没有多大意义,因为您不会说这里的调用上下文(渲染器)包含实例中的该信息。渲染器本身没有“要链接到的 URL”,因此不应将其隐藏在实例变量中。The reason instance variables work on classes in Ruby is that Ruby classes are instances themselves (instances of class Class). Try it for yourself by inspecting
DummyClass.class
. There are no "static methods" in the C# sense in Ruby because every method is defined on (or inherited into) some instance and invoked on some instance. Accordingly, they can access whatever instance variables happen to be available on the callee.Since
DummyClass
is an instance, it can have its own instance variables just fine. You can even access those instance variables so long as you have a reference to the class (which should be always because class names are constants). At any point, you would be able to call::DummyClass.instance_variable_get(:@arr)
and get the current value of that instance variable.As for whether it's a good thing to do, it depends on the methods.
If
@arr
is logically the "state" of the instance/classDummyClass
, then store it in instance variable. If@arr
is only being used indummy_method2
as an operational shortcut, then pass it as an argument. To give an example where the instance variable approach is used, consider ActiveRecord in Rails. It allows you to do this:Here, the name that has been assigned to the user is data that is legitimately on the user. If, before the
#save
call, one were to ask "what is the name of the user at this point", you would answer "foobar". If you dig far enough into the internals (you'll dig very far and into a lot of metaprogramming, you'll find that they use instance variables for exactly this).The example I've used contains two separate public invocations. To see a case where instance variables are still used despite only one call being made, look at the ActiveRecord implementation of
#update_attributes
. The method body is simplyload(attributes, false) && save
. Why does#save
not get passed any arguments (like the newname
) even though it is going to be in the body of save where something likeUPDATE users SET name='foobar' WHERE id=1;
? It's because stuff like the name is information that belongs on the instance.Conversely, we can look at a case where instance variables would make no sense to use. Look at the implementation of
#link_to_if
, a method that accepts a boolean-ish argument (usually an expression in the source code) alongside arguments that are ordinarily accepted by#link_to
such as the URL to link to. When the boolean condition is truthy, it needs to pass the rest of the arguments to#link_to
and invoke it. It wouldn't make much sense to assign instance variables here because you would not say that the invoking context here (the renderer) contains that information in the instance. The renderer itself does not have a "URL to link to", and consequently, it should not be buried in an instance variable.这些是类实例变量,在 ruby 中是完全合法的:类也是对象(类的实例),因此也有实例变量。
需要注意的一件事是,每个子类都有自己的一组类实例变量(毕竟这些是不同的对象):如果您对
DummyClass
进行子类化,则子类上的类方法将无法请参阅@arr。类变量 (
@@foo
) 当然是相反的:整个类层次结构共享相同的类变量。Those are class instance variables and are a perfectly legitimate things in ruby: classes are objects too (instances of Class) and so have instance variables.
One thing to look out for is that each subclass will have its own set of class instance variables (after all these are different objects): If you subclassed
DummyClass
, class methods on the subclass would not be able to see@arr
.Class variables (
@@foo
) are of course the other way round: the entire class hierarchy shares the same class variables.