Переменные экземпляра класса Ruby и наследование

Мне назвали класс Ruby LibraryItem. Я хочу связать с каждым экземпляром этого класса массив атрибутов. Этот массив долог и смотрит что-то как

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

Обратите внимание, что эти атрибуты, как действительно предполагается, не являются методами, просто списком атрибутов это a LibraryItem имеет.

Затем, я хочу сделать подкласс LibraryItem названный LibraryBook это имеет массив атрибутов, который включает все атрибуты LibraryItem но будет также включать намного больше.

В конечном счете я захочу несколько подклассов LibraryItem каждый с их собственной версией массива @attributes но каждое прибавление к LibraryItem @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

Если бы это должно было работать, то я хотел бы что-то вроде этого

puts LibraryItem.attributes 
puts LibraryBook.attributes

производить

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

(Добавленный 02 мая 2010), Одно решение этого состоит в том, чтобы сделать @attributes простая переменная экземпляра и затем добавляет новые атрибуты для LibraryBoot в initialize метод (это было предложено демами в одном из ответов).

В то время как это, конечно, работало бы (и, на самом деле, что я делал все время), я не доволен этим, поскольку это является субоптимальным: почему эти неизменные массивы должны быть созданы каждый раз, когда объект создается?

То, что я действительно хочу, должно иметь переменные класса, которые могут наследоваться родительскому классу, но при изменении в дочернем классе не изменяются в родительский класс.

18
задан rlandster 4 May 2010 в 03:32
поделиться

6 ответов

Поскольку вы упомянули, что атрибуты являются «фиксированными» и «неизменными», я предполагаю Вы имеете в виду, что никогда не измените их значение после создания объекта. В этом случае должно работать что-то вроде следующего:

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 , которая имеет дополнительные значения в подклассе.

9
ответ дан 30 November 2019 в 07:28
поделиться

В LibraryBook переменная @attributes является новой независимой переменной, переменной экземпляра объекта LibraryBook, поэтому она не инициализируется и вы получаете ошибку "undefined method ... for nil"
. Вы должны инициализировать ее списком атрибутов LibraryItem перед использованием

class LibraryBook < LibraryItem
  @attributes = LibraryItem::attributes + ['ISBN', 'pages']
end
2
ответ дан 30 November 2019 в 07:28
поделиться

Вы также можете сделать это с помощью CINSTANTS. Хотя нет проверки.

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

class LibraryBook < LibraryItem
  ATTRIBUTES .push('ISBN', 'pages']
end
-1
ответ дан 30 November 2019 в 07:28
поделиться

Как версия:

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

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

b = LibraryBook.new
5
ответ дан 30 November 2019 в 07:28
поделиться

Из любопытства, сработает ли что-то подобное?

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

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

Похоже, это даст желаемый результат - массив ATTRIBUTES раскрывается при создании объекта класса, и значения АТРИБУТОВ меняются, как и ожидалось:

> Foo::ATTRIBUTES
=> ['title','authors','location'] 
> Bar::ATTRIBUTES
=> ['title','authors','location', 'ISBN', 'pages'] 
4
ответ дан 30 November 2019 в 07:28
поделиться

ActiveSupport имеет метод class_attribute на краю рельсов.

3
ответ дан 30 November 2019 в 07:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: