Ruby:继承与类变量一起使用的代码

情况:我有多个类,每个类应包含一个带有配置哈希的变量; 每个类的散列都不同,但一个类的所有实例都相同。

起初,我尝试过这样

class A
  def self.init config
    @@config = config
  end

  def config
    @@config
  end
end

class B < A; end
class C < A; end

但是很快注意到,它不会那样工作,因为@@ config是在A而不是B或C的上下文中保存的,因此:

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"

我想到这样使用它:

modules = [B, C]
modules.each do |m|
  m.init(@config[m.name])
end
# ...
B.new  # which should then have the correct config

现在,我很清楚为什么会发生这种情况,但是我不确定这种情况的原因。

它不能以其他方式工作,将类变量保存在子类的上下文中吗?

我还感到恼火的是,即使被称为“超类”,自我始终是子类。 因此,我首先希望超类的代码“在子类的上下文中执行”。

对此的一些启示将不胜感激。

另一方面,我可能不得不接受这种方式的工作,并且我必须找到另一种方式来做到这一点。

有没有一种“元”方式可以做到这一点? (我尝试了class_variable_set等,但没有运气)

或者,也许该“ init”方法的整个想法首先存在缺陷,并且还有其他“模式”可以做到这一点?

我可以将@@ config设置为哈希,保留所有配置,并始终选择正确的配置,但是我发现这有点尴尬..(不是通过继承来解决此类问题吗?;)

MPelletier asked 2020-08-10T21:36:59Z
1个解决方案
109 votes

A.init不是类别变量。 它们是类层次结构变量,即它们在整个类层次结构(包括所有子类和所有子类的所有实例)之间共享。 (建议人们应该更像$$variables来思考A.config=,因为与$globals相比,实际上与$globals有更多的共同点。这种方式可以减少混乱。其他人则走得更远,建议将其从语言中删除。 )

Ruby没有类变量,也就是说,Java(在这里称为静态字段)具有类变量。 它不需要类变量,因为类也是对象,因此它们可以像其他任何对象一样具有实例变量。 您所要做的就是删除多余的A.inits。 (并且您将必须为类实例变量提供访问器方法。)

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

让我们简化一下,因为A.init显然只是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
  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"

但是,如果您真的需要的话,那么我无法撼动这种感觉,那就是该设计还有一些根本性的疑问。

Jörg W Mittag answered 2020-08-10T21:37:24Z
translate from https://stackoverflow.com:/questions/1251352/ruby-inherit-code-that-works-with-class-variables