Ruby:继承类变量的代码 [英] Ruby: Inherit code that works with class variables
问题描述
情况:我有多个类,应该每个都拥有一个配置散列的变量;
一开始,我尝试过这样
class A
def self.init config
@@ config = config
end
def config
@@ config
end
end
class B<一个; end
class C<一个; end
但很快注意到它不会工作,因为@@ config是在上下文A,而不是B或C,因此:
B.initbar
p B.new.config# => bar
p C.new.config#=> bar - 如果B有它自己的话就是nil @@ config
C.initfoo
p B.new.config#=> foo - 如果C有它自己的话,它仍然是bar。@@ config
p C.new.config#=> foo
我想像这样使用它:
modules = [B,C]
modules.each do | m |
m.init(@config [m.name])
end
#...
B.new#然后应该有正确的配置
现在,我清楚地知道为什么会发生这种情况,但我不知道这是什么原因。 p>
不能使用其他方法,在子类的上下文中保存类变量吗?
我也发现刺激是事实,self是总是子类,即使被称为在超类。从这个,我首先期望超类的代码是在子类的上下文中执行。
一些启发对此将非常感激。
另一方面,我可能必须接受它的工作方式,我必须找到另一种方式来做到这一点。
有没有一个元的方式来做到这一点? (我尝试用class_variable_set等,但没有运气)
或者也许是'init'方法的缺点在第一位,有一些其他模式这样做?
我可以使@@ config哈希,持有所有的配置,总是选择正确的一个,但我发现有点尴尬.. (不是继承有解决这种问题吗?)
@@ variables
不是类变量。它们是类层次结构变量,即它们在整个类层次结构之间共享,包括所有子类和所有子类的所有实例。 (有人建议应该考虑 @@ variables
更像 $$ variables
,因为他们实际上有更多与 $ globals
的共同之处在于 @ivars
,这样更容易造成混乱,应该简单地从语言中删除。)
Ruby没有类变量,例如,Java(它们称为静态字段)有它们。它不需要类变量,因为类也是对象,因此它们可以像任何其他对象一样具有实例变量。你所要做的就是删除无关的 @
。 (您必须为类实例变量提供一个访问器方法。)
class A
def self。 init config
@config = config
end
def self.config#这是从外部访问所需的
@config
end
def config
self.class.config#这将调用self的类上面的访问器
end
end
让我们简化一下,因为 A.config
显然只是一个attribute_reader:
class A
class<< self
def init config
@config = config
end
attr_reader:config
end
def config
self.class.config
end
end
A.init
只是一个有名字的作家,所以让我们将它重命名为 A.config =
一个作家,这反过来意味着我们的一对方法现在只是一个访问器对。 (因为我们改变了API,测试代码也必须改变。)
class A
<< self
attr_accessor:config
end
def config
self.class.config
end
end
B类一个; end
class C<一个; end
B.config =bar
p B.new.config#=> bar
p C.new.config#=> nil
C.config =foo
p B.new.config#=> bar
p C.new.config#=> foo
但是,我不能动摇那种更基本的东西iffy设计,如果你需要这一点。
The situation: I have multiple classes that should each hold a variable with a configuration hash; a different hash for each class but the same for all instances of a class.
At first, i tried like this
class A
def self.init config
@@config = config
end
def config
@@config
end
end
class B < A; end
class C < A; end
But soon noticed that it wouldn't work that way because @@config is held in the context of A, not B or C, thus:
B.init "bar"
p B.new.config # => "bar"
p C.new.config # => "bar" - which would be nil if B had it's own @@config
C.init "foo"
p B.new.config # => "foo" - which would still be "bar" if C had it's own @@config
p C.new.config # => "foo"
I thought of using it like this:
modules = [B, C]
modules.each do |m|
m.init(@config[m.name])
end
# ...
B.new # which should then have the correct config
Now, it's clear to me why that happens, but I'm not sure about the reason for it being like this.
Couldn't it work the other way too, holding the class variable in the context of the subclass?
What i also found irritating was the fact that self is always the subclass even when called 'in' the superclass. From this, I first expected the code from the superclass is "executed in the context of" the subclass.
Some enlightenment about this would be greatly appreciated.
On the other hand, I likely have to accept it works that way and that I have to find another way to do this.
Is there a "meta" way to do this? (I tried with class_variable_set etc. but with no luck)
Or maybe is the whole idea of that 'init' method flawed in the first place and there's some other "pattern" to do this?
I could just make @@config a hash, holding all the configs and always pick the right one, but I find that a little awkward.. (isn't inheritance there to solve this kind of problem? ;)
The @@variables
aren't class variables. They are class hierarchy variables, i.e. they are shared between the entire class hierarchy, including all subclasses and all instances of all subclasses. (It has been suggested that one should think of @@variables
more like $$variables
, because they actually have more in common with $globals
than with @ivars
. That way lies less confusion. Others have gone further and suggest that they should simply be removed from the language.)
Ruby doesn't have class variables in the sense that, say, Java (where they are called static fields) has them. It doesn't need class variables, because classes are also objects, and so they can have instance variables just like any other object. All you have to do is to remove the extraneous @
s. (And you will have to provide an accessor method for the class instance variable.)
class A
def self.init config
@config = config
end
def self.config # This is needed for access from outside
@config
end
def config
self.class.config # this calls the above accessor on self's class
end
end
Let's simplify this a bit, since A.config
is clearly just an attribute_reader:
class A
class << self
def init config
@config = config
end
attr_reader :config
end
def config
self.class.config
end
end
And, in fact, A.init
is just a writer with a funny name, so let's rename it to A.config=
and make it a writer, which in turn means that our pair of methods is now just an accessor pair. (Since we changed the API, the test code has to change as well, obviously.)
class A
class << self
attr_accessor :config
end
def config
self.class.config
end
end
class B < A; end
class C < A; end
B.config = "bar"
p B.new.config # => "bar"
p C.new.config # => nil
C.config = "foo"
p B.new.config # => "bar"
p C.new.config # => "foo"
However, I can't shake the feeling that there is something more fundamentally iffy about the design, if you need this at all.
这篇关于Ruby:继承类变量的代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!