Ruby:继承类变量的代码 [英] Ruby: Inherit code that works with class variables

查看:95
本文介绍了Ruby:继承类变量的代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

情况:我有多个类,应该每个都拥有一个配置散列的变量;



一开始,我尝试过这样

  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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆