繁体   English   中英

在Ruby中初始化类实例变量

[英]Initializing class instance variables in Ruby

我正在开发一个小型应用程序,并且遇到了ruby的OOP模型的问题。 我有以下简化的类结构。

class Foo
  protected
    @bar = []
    def self.add_bar(val)
      @bar += val
    end
    def self.get_bar
      @bar
    end
end

class Baz < Foo
  add_bar ["a", "b", "c"]
end

我现在的问题是,当我在Baz的类定义中调用add_bar时, @bar显然没有初始化,并且我得到一个错误, +运算符不可用于nil 直接在Foo上调用add_bar不会产生此问题。 为什么这样,我如何正确初始化@bar

为了弄清楚我想要什么,我将指出我期望从这些类中获得的行为。

Foo.add_bar ["a", "b"]
Baz.add_bar ["1", "2"]
Foo.get_bar # => ["a", "b"]
Baz.get_bar # => ["a", "b", "1", "2"]

我怎么能实现这个目标?

简短回答:实例变量不会被子类继承

更长的答案:问题是你在类的主体中写了@bar = [] (在任何方法之外)。 设置实例变量时,它存储在当前self任何内容中。 当你在一个类体中时, self就是类对象Foo。 因此,在您的示例中, @foo foo在类对象Foo上定义。

稍后,当您尝试查找实例变量时,Ruby会查找当前self 当你从Baz调用add_bar时, self就是Baz。 在add_bar的主体中, self也是STILL Baz(即使该方法在Foo中)。 所以,Ruby在Baz中查找@bar并且找不到它(因为你在Foo中定义它)。

这是一个可能使这个更清晰的例子

class Foo
  @bar = "I'm defined on the class object Foo. self is #{self}"

 def self.get_bar
    puts "In the class method. self is #{self}"    
    @bar
  end

  def get_bar
    puts "In the instance method. self is #{self} (can't see @bar!)"
    @bar
  end
end

>> Foo.get_bar
In the class method. self is Foo
=> "I'm defined on the class object Foo. self is Foo"

>> Foo.new.get_bar
In the instance method. self is #<Foo:0x1056eaea0> (can't see @bar!)
=> nil

这无疑是有点令人困惑的,也是Ruby新手的常见绊脚石,所以不要感到难过。 当我阅读编程Ruby (又名“The Pickaxe”)中的“Metaprogramming”章节时,这个概念终于点击了我。

我如何解决你的问题:看看Rails的class_attribute方法。 它允许你尝试做的事情(在父类中定义一个可以在其子类中继承(和覆盖)的属性)。

好吧,因为@bar被定义为类实例变量,所以它仅限于类Foo。 检查一下:

class Foo
    @bar = []
end

class Baz < Foo
end

Foo.instance_variables #=> [:@bar]
Baz.instance_variables #=> []

无论如何,对于这个简单的例子,你可以这样做:

class Foo
  protected
    def self.add_bar(val)
      @bar ||=[]
      @bar += val
    end
    def self.get_bar
      @bar
    end
end

class Baz < Foo
  add_bar ["a", "b", "c"]
end

在这里阅读更多相关信息。

我是这样做的:

class Base

    class << self

        attr_accessor :some_var


        def set_some_var(value)
            self.some_var = value
        end

    end

end


class SubClass1 < Base
  set_some_var :foo
end

class SubClass2 < Base
  set_some_var :bar
end

然后它应该做你想要的。

[8] pry(main)> puts SubClass1.some_var
foo
[9] pry(main)> puts SubClass2.some_var
bar

请注意,set_some_var方法是可选的,如果您愿意,可以执行SubClass1.some_var = ...

如果你想要一些默认值,可以在class << self下添加类似的东西

def some_var
    @some_var || 'default value'
end

这似乎运作良好:

class Foo
  protected
  @@bar = {}
  def self.add_bar(val)
    @@bar[self] ||= []
    @@bar[self] += val
  end
  def self.get_bar
    (self == Foo ? [] : @@bar[Foo] || []) + (@@bar[self] || [])
  end
end
class Baz < Foo
end

Foo.add_bar ["a", "b"]
Baz.add_bar ["1", "2"]
Foo.get_bar     # => ["a", "b"]
Baz.get_bar     # => ["a", "b", "1", "2"]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM