Ruby类实例变量和继承

时间:2010-05-02 06:22:13

标签: ruby class instance-variables

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

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

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

接下来,我想创建一个名为LibraryItem的{​​{1}}子类,其子类包含LibraryBook的所有属性,但也包含更多属性。

最终我会想要几个LibraryItem的子类,每个子类都有自己的数组LibraryItem,但每个子类都添加到@attributes的{​​{1}}(例如LibraryItem 1}},@attributesLibraryBook等。)

所以,这是我的尝试:

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在其中一个答案中建议的)。

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

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

9 个答案:

答案 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”
您应该在使用

之前通过LibraryItem attribut的列表对其进行初始化
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
相关问题