Ruby class instance variables and inheritance

岁酱吖の 提交于 2019-11-28 22:44:40

问题


I have a Ruby class called LibraryItem. I want to associate with every instance of this class an array of attributes. This array is long and looks something like

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

Note that these attributes are not really supposed to be methods, just a list of attributes that a LibraryItem has.

Next, I want to make a subclass of LibraryItem called LibraryBook that has an array of attributes that includes all the attributes of LibraryItem but will also include many more.

Eventually I will want several subclasses of LibraryItem each with their own version of the array @attributes but each adding on to LibraryItem's @attributes (e.g., LibraryBook, LibraryDVD, LibraryMap, etc.).

So, here is my attempt:

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

class LibraryBook < LibraryItem
  @attributes.push('ISBN', 'pages')
end

This does not work. I get the error

undefined method `push' for nil:NilClass

If it were to work, I would want something like this

puts LibraryItem.attributes 
puts LibraryBook.attributes

to output

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

(Added 02-May-2010) One solution to this is to make @attributes a simple instance variable and then add the new attributes for LibraryBoot in the initialize method (this was suggested by demas in one of the answers).

While this would certainly work (and is, in fact, what I have been doing all along), I am not happy with this as it is sub-optimal: why should these unchanging arrays be constructed every time an object is created?

What I really want is to have class variables that can inherit from a parent class but when changed in the child class do not change in the the parent class.


回答1:


Since you mention that the attributes are "fixed" and "unchanging", I am assuming that you mean that you will never change their value once the object is created. In that case, something like the following should work:

class Foo
    ATTRS = ['title', 'authors', 'location']
    def attributes
        ATTRS
    end
end

class Bar < Foo
    ATTRS = ['ISBN', 'pages']
    def attributes
        super + ATTRS
    end
end

You are manually implementing a reader method (instead of letting attr_accessor create it for you) that disguises the internal name of the array. In your subclass, you simply call the ancestor class' reader function, tack on the additional fields associated with the child class, and return that to the caller. To the user, this appears like a read-only member variable named attributes that has additional values in the sub-class.




回答2:


Another solution would be to use the inherited hook:

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



回答3:


Just as a version:

class LibraryItem < Object
  def initialize
    @attributes = ['one', 'two'];
  end
end

class LibraryBook < LibraryItem
  def initialize
   super
   @attributes.push('three')
 end
end

b = LibraryBook.new



回答4:


Out of curiosity, will something like this work?

class Foo
  ATTRIBUTES = ['title','authors','location']
end

class Bar < Foo
  ATTRIBUTES |= ['ISBN', 'pages']
end

This would seem to produce the desired result - the ATTRIBUTES array is expanded when the class object is created, and the values of ATTRIBUTES varies as expected:

> Foo::ATTRIBUTES
=> ['title','authors','location'] 
> Bar::ATTRIBUTES
=> ['title','authors','location', 'ISBN', 'pages'] 



回答5:


ActiveSupport has class_attribute method in rails edge.




回答6:


To expand on @Nick Vanderbilt's answer, using active_support you do this, which is exactly the short hand I want for this functionality. Here's a complete example:

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"]

Shame it's so difficult for ruby to achieve this without needing a library for it. It's the only thing I miss from python. And in my case, I don't mind the dependency on the active_support gem.




回答7:


In LibraryBook variable @attributes is a new independent variable, instance variable of object LibraryBook, so its not initialized and you get error "undefined method ... for nil"
You should to initialize it by LibraryItem attribut's list before using

class LibraryBook < LibraryItem
  @attributes = LibraryItem::attributes + ['ISBN', 'pages']
end



回答8:


This is for strings (anything really), rather than arrays, but...

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"]



回答9:


You can do it using CINSTANTS also. No check though.

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

class LibraryBook < LibraryItem
  ATTRIBUTES .push('ISBN', 'pages']
end


来源:https://stackoverflow.com/questions/2752641/ruby-class-instance-variables-and-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!