我有一个名为LibraryItem
的Ruby类。我想将此类的每个实例与一组属性相关联。这个数组很长,看起来像
['title', 'authors', 'location', ...]
请注意,这些属性实际上不应该是方法,只是LibraryItem
具有的属性列表。
接下来,我想创建一个名为LibraryItem
的{{1}}子类,其子类包含LibraryBook
的所有属性,但也包含更多属性。
最终我会想要几个LibraryItem
的子类,每个子类都有自己的数组LibraryItem
,但每个子类都添加到@attributes
的{{1}}(例如LibraryItem
1}},@attributes
,LibraryBook
等。)
所以,这是我的尝试:
LibraryDVD
这不起作用。我收到了错误
LibraryMap
如果要工作,我会想要这样的东西
class LibraryItem < Object
class << self; attr_accessor :attributes; end
@attributes = ['title', 'authors', 'location',]
end
class LibraryBook < LibraryItem
@attributes.push('ISBN', 'pages')
end
输出
undefined method `push' for nil:NilClass
(2010年5月2日添加)
对此的一个解决方案是使puts LibraryItem.attributes
puts LibraryBook.attributes
成为一个简单的实例变量,然后在['title', 'authors', 'location']
['title', 'authors', 'location', 'ISBN', 'pages']
方法中添加@attributes
的新属性(这是demas在其中一个答案中建议的)。
虽然这肯定会起作用(实际上是我一直在做的事情),但我对此并不满意,因为它是次优的:为什么每次创建一个对象时都要构造这些不变的数组?
我真正想要的是拥有可以从父类继承的类变量,但是在子类中更改时,在父类中不会更改。
答案 0 :(得分:10)
另一种解决方案是使用继承的钩子:
class LibraryItem < Object
class << self
attr_accessor :attributes
def inherit_attributes(attrs)
@attributes ||= []
@attributes.concat attrs
end
def inherited(sublass)
sublass.inherit_attributes(@attributes)
end
end
@attributes = ['title', 'authors', 'location',]
end
class LibraryBook < LibraryItem
@attributes.push('ISBN', 'pages')
end
答案 1 :(得分:9)
既然你提到属性是“固定的”和“不变的”,我假设你的意思是你创建对象后永远不会改变它们的值。在这种情况下,类似下面的内容应该有效:
class Foo
ATTRS = ['title', 'authors', 'location']
def attributes
ATTRS
end
end
class Bar < Foo
ATTRS = ['ISBN', 'pages']
def attributes
super + ATTRS
end
end
您正在手动实现一种读取器方法(而不是让attr_accessor
为您创建),它伪装了数组的内部名称。在您的子类中,您只需调用祖先类的reader函数,添加与子类关联的其他字段,并将其返回给调用者。对于用户来说,这看起来像一个名为attributes
的只读成员变量,在子类中有其他值。
答案 2 :(得分:5)
就像版本一样:
class LibraryItem < Object
def initialize
@attributes = ['one', 'two'];
end
end
class LibraryBook < LibraryItem
def initialize
super
@attributes.push('three')
end
end
b = LibraryBook.new
答案 3 :(得分:4)
出于好奇,会有类似的工作吗?
class Foo
ATTRIBUTES = ['title','authors','location']
end
class Bar < Foo
ATTRIBUTES |= ['ISBN', 'pages']
end
这似乎会产生所需的结果 - 在创建类对象时会扩展ATTRIBUTES数组,并且ATTRIBUTES的值会按预期变化:
> Foo::ATTRIBUTES
=> ['title','authors','location']
> Bar::ATTRIBUTES
=> ['title','authors','location', 'ISBN', 'pages']
答案 4 :(得分:3)
ActiveSupport在rails edge中有class_attribute方法。
答案 5 :(得分:3)
要扩展@Nick Vanderbilt的答案,请使用active_support执行此操作,这正是我想要此功能的简短操作。这是一个完整的例子:
require 'active_support/core_ext'
class Foo
class_attribute :attributes
self.attributes = ['title','authors','location']
end
class Bar < Foo
self.attributes = Foo.attributes + ['ISBN', 'pages']
end
puts Foo.attributes.inspect #=> ["title", "authors", "location"]
puts Bar.attributes.inspect #=> ["title", "authors", "location", "ISBN", "pages"]
羞耻的是,ruby很难实现这一点而不需要一个库。这是我从python中唯一遗漏的东西。就我而言,我不介意依赖于active_support gem。
答案 6 :(得分:2)
在LibraryBook变量中@attributes是一个新的自变量,对象LibraryBook的实例变量,所以它没有初始化,你得到错误“undefined method ... for nil”
您应该在使用
class LibraryBook < LibraryItem
@attributes = LibraryItem::attributes + ['ISBN', 'pages']
end
答案 7 :(得分:0)
这是针对字符串(任何事情),而不是数组,但是......
class A
def self.a
@a || superclass.a rescue nil
end
def self.a=(value)
@a = value
end
self.a = %w( apple banana chimp )
end
class B < A
end
class C < B
self.a += %w( dromedary elephant )
end
class D < A
self.a = %w( pi e golden_ratio )
end
irb(main):001:0> require 'test2'
=> true
irb(main):002:0> A.a
=> ["apple", "banana", "chimp"]
irb(main):003:0> B.a
=> ["apple", "banana", "chimp"]
irb(main):004:0> C.a
=> ["apple", "banana", "chimp", "dromedary", "elephant"]
irb(main):005:0> D.a
=> ["pi", "e", "golden_ratio"]
irb(main):006:0> A.a = %w( 7 )
=> ["7"]
irb(main):007:0> A.a
=> ["7"]
irb(main):008:0> B.a
=> ["7"]
irb(main):009:0> C.a = nil
=> nil
irb(main):010:0> C.a
=> ["7"]
答案 8 :(得分:-1)
您也可以使用CONSTANTS。没有检查。
class LibraryItem < Object
class << self; attr_accessor :attributes; end
ATTRIBUTES = ['title', 'authors', 'location',]
end
class LibraryBook < LibraryItem
ATTRIBUTES .push('ISBN', 'pages']
end