我正在开发一个小型应用程序,并且遇到了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
显然没有初始化,我收到+
运算符不可用于{{1}的错误}。直接在nil
上调用add_bar
不会产生此问题。为什么这样,我如何正确初始化Foo
?
为了弄清楚我想要什么,我会指出我期望从这些课程中获得的行为。
@bar
我怎么能实现这个目标?
答案 0 :(得分:42)
简答:实例变量不会被子类继承
更长的回答:问题是您在类的正文中写了@bar = []
(在任何方法之外)。设置实例变量时,它存储在当前self
的任何内容中。当你在一个类体中时,self
是类对象Foo。因此,在您的示例中,@foo
在类对象Foo上定义。
稍后,当您尝试查找实例变量时,Ruby会查找当前self
的内容。当你从Baz调用add_bar时,self
是Baz。另外self
在add_bar的主体中是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
方法。它允许你尝试做的事情(在父类中定义一个可以在其子类中继承(和覆盖)的属性)。
答案 1 :(得分:3)
好吧,因为@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
详细了解此here。
答案 2 :(得分:2)
我是这样做的:
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
答案 3 :(得分:1)
这似乎运作良好:
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"]