【问题标题】:How do I override a variable in a Ruby subclass without affecting the superclass?如何在不影响超类的情况下覆盖 Ruby 子类中的变量?
【发布时间】:2013-10-15 13:34:09
【问题描述】:

假设我有一个包含一些“静态”变量的类。我希望该类的子类能够覆盖这些变量而不影响原始类。使用类变量是不可能的,因为这些变量似乎在子类和超类之间共享:

class Foo
  @@test = "a"

  def speak; puts @@test; end
end

class Bar < Foo
  @@test = "b"
end

Bar.new.speak
# b

Foo.new.speak
# b

也不可能使用常量:

class Foo
  TEST = "a"

  def speak; puts TEST; end
end

class Bar < Foo
  TEST = "b"
end

Bar.new.speak
# a

Foo.new.speak
# a

超类中定义的方法忽略子类中的常量。

显而易见的解决方法是为需要“覆盖”的变量定义方法:

class Foo
  def test; "a"; end
end

但这感觉就像一个黑客。我觉得使用类变量应该可以做到这一点,而且我可能只是做错了。例如,当我将Object 子类化时(默认情况下会发生这种情况):

class Foo < Object
  @@bar = 123
end

Object.class_variable_get(:@@bar)
# NameError: uninitialized class variable @@bar in Object

为什么@@bar 不像上面的Bar &lt; Foo 示例中那样设置在Object 上?


总结一下:如何在不影响超类的情况下覆盖子类中的变量?

【问题讨论】:

    标签: ruby


    【解决方案1】:

    类常量做你想做的事,你只需要以不同的方式使用它们:

    class Foo
      TEST = "a"
    
      def speak
        puts self.class::TEST
      end
    end
    
    class Bar < Foo
      TEST = "b"
    end
    
    Bar.new.speak # => a
    Foo.new.speak # => b
    

    【讨论】:

    • 编辑了答案的文字以使其更有意义,希望您不要介意。这对我来说似乎是最正确的方法,所以我接受答案。
    • @Codemonkey 您最初的问题是关于变量,而不是常量。尽管 Ruby 允许您覆盖常量,但这样做会收到警告,并且会让任何阅读您的代码的人感到困惑。 Denis 的答案是实现每个类变量的正确方法。
    • @Max:如果您阅读了整个问题,很明显我所追求的只是一种覆盖与子类中的类相关的 的方法 - 它是无论是变量还是常量都不重要。我同意“变量”可能是一个糟糕的词选择。在我看来,使用这样的常量比定义元类属性读取器更直接和直观。在子类中定义与其超类中的常量同名的常量,我没有收到任何警告(就像在这个答案的例子中一样)
    • 在子类中定义新常量时没有收到警告是正确的。此外,如果您在此答案中定义 speak,则无需在子类中重新定义它。
    • @Max 谢谢! .是的,没有必要重新定义 speak 。修改了答案。更少的代码:)
    【解决方案2】:

    向类本身添加一个变量(如在类实例中,而不是类变量):

    class Foo
      @var = 'A'
    
      class << self
        attr_reader :var
      end
    
      def var
        self.class.var
      end
    end
    
    class Bar < Foo
      @var = 'B'
    end
    
    Foo.var # A
    Foo.new.var # A
    Bar.var # B
    Bar.new.var # B
    

    【讨论】:

    • 我喜欢这种方法!
    【解决方案3】:

    正确的方法(恕我直言)是使用方法,因为这样你就可以使用继承和虚拟调度,就像你想要的那样。

    类变量在层次结构中向下共享,而不是向上共享。这就是@@barObject 中不可用的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-24
      • 2016-02-28
      • 2016-09-19
      • 1970-01-01
      • 1970-01-01
      • 2021-02-10
      • 1970-01-01
      相关资源
      最近更新 更多