18

我有一个名为LibraryItem. 我想与这个类的每个实例关联一个属性数组。这个数组很长,看起来像

['title', 'authors', 'location', ...]

请注意,这些属性实际上并不应该是方法,而只是 a 具有的属性列表LibraryItem

接下来,我想创建一个LibraryItem被调用的子类,LibraryBook它有一个属性数组,其中包括所有属性,LibraryItem但还会包括更多属性。

最终我会想要几个子类,LibraryItem每个子类都有自己的数组版本,@attributes但每个子类都添加到LibraryItem's @attributes(例如,、、、LibraryBookLibraryDVDLibraryMap

所以,这是我的尝试:

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

如果它起作用,我想要这样的东西

puts LibraryItem.attributes 
puts LibraryBook.attributes

输出

['title', 'authors', 'location']
['title', 'authors', 'location', 'ISBN', 'pages']

(添加于 2010 年 5 月 2 日)对此的一种解决方案是创建@attributes一个简单的实例变量,然后LibraryBootinitialize方法中添加新属性(这是 demas 在其中一个答案中提出的建议)。

虽然这肯定会奏效(事实上,这也是我一直在做的事情),但我对此并不满意,因为它不是最理想的:为什么每次创建对象时都要构造这些不变的数组?

我真正想要的是拥有可以从父类继承但在子类中更改时不会在父类中更改的类变量。

4

9 回答 9

12

另一种解决方案是使用继承的钩子:

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
于 2012-11-29T16:01:47.007 回答
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数组内部名称的读取器方法(而不是让您为您创建它)。在您的子类中,您只需调用祖先类的读取器函数,添加与子类关联的附加字段,然后将其返回给调用者。对用户来说,这看起来像是一个名为的只读成员变量attributes,在子类中具有附加值。

于 2010-08-11T22:38:27.870 回答
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
于 2010-05-02T07:10:59.743 回答
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'] 
于 2010-08-11T21:45:06.083 回答
4

要扩展@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 的依赖。

于 2012-02-27T04:57:57.677 回答
3

ActiveSupport 在 rails edge 有 class_attribute 方法。

于 2010-08-12T02:26:14.227 回答
2

在LibraryBook变量@attributes是一个新的自变量,对象LibraryBook的实例变量,所以它没有被初始化并且你得到错误“undefined method ... for nil”
你应该在使用之前通过LibraryItem属性列表初始化它

class LibraryBook < LibraryItem
  @attributes = LibraryItem::attributes + ['ISBN', 'pages']
end
于 2010-05-02T07:42:01.867 回答
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"]
于 2012-04-11T22:23:58.753 回答
-1

您也可以使用 CONSTANTS 来完成。虽然没有检查。

class LibraryItem < Object
  class << self; attr_accessor :attributes; end
  ATTRIBUTES = ['title', 'authors', 'location',]
end

class LibraryBook < LibraryItem
  ATTRIBUTES .push('ISBN', 'pages']
end
于 2010-05-02T06:26:19.620 回答