Zmienne instancji klasy Ruby i dziedziczenie

Mam klasę Ruby o nazwie LibraryItem. Chcę powiązać z każdą instancją tej klasy tablicę atrybutów. Tablica ta jest długa i wygląda jak

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

Zauważ, że te atrybuty tak naprawdę nie powinny być metodami, a jedynie listą atrybutów, które posiada LibraryItem.

Następnie chcę utworzyć podklasę LibraryItem o nazwie LibraryBook, która ma tablicę atrybutów, która zawiera wszystkie atrybuty LibraryItem, ale będzie zawierać również wiele innych.

Ostatecznie I będzie chciał kilka podklas LibraryItem, każda z własną wersją tablicy @attributes, ale każda dodawana do LibraryItem ' s @attributes (np., LibraryBook, LibraryDVD, LibraryMap, itd.).

Więc oto moja próba:
class LibraryItem < Object
  class << self; attr_accessor :attributes; end
  @attributes = ['title', 'authors', 'location',]
end

class LibraryBook < LibraryItem
  @attributes.push('ISBN', 'pages')
end
To nie działa. I get the error
undefined method `push' for nil:NilClass
Gdyby to zadziałało, chciałbym coś takiego.]}
puts LibraryItem.attributes 
puts LibraryBook.attributes

Do wyjścia

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

(Dodano 02-May-2010) Jednym z rozwiązań jest utworzenie @attributes prostej zmiennej instancji, a następnie dodanie nowych atrybutów dla {[18] } w metodzie initialize (zasugerował to demas w jednej z odpowiedzi).

O ile to z pewnością zadziała (i jest w rzeczywistości tym, co robiłem przez cały czas), nie jestem z tego zadowolony, ponieważ jest to nieoptymalne: dlaczego te niezmienne tablice powinny być konstruowane za każdym razem, gdy tworzony jest obiekt?

To, czego naprawdę chcę, to mieć zmienne klasowe, które mogą dziedziczyć po klasie rodzica, ale gdy zostaną zmienione w klasie potomnej, nie zmienią się w klasie rodzica.

Author: rlandster, 2010-05-02

9 answers

Skoro wspominasz, że atrybuty są "stałe" i "niezmienne", zakładam, że masz na myśli, że nigdy nie zmienisz ich wartości po utworzeniu obiektu. W takim przypadku powinno działać coś takiego jak:

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

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

Ręcznie implementujesz metodę reader (zamiast pozwolić attr_accessor utworzyć ją dla Ciebie), która zamaskuje wewnętrzną nazwę tablicy. W podklasie po prostu wywołujesz funkcję czytnika klasy przodka, zaznaczając dodatkowe pola związane z klasą dziecięcą i zwróć to rozmówcy. Dla użytkownika wygląda to jak zmienna składowa tylko do odczytu o nazwie attributes, która ma dodatkowe wartości w podklasie.

 9
Author: bta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 22:38:27

Innym rozwiązaniem byłoby użycie odziedziczonego Hooka:

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
 10
Author: tomazy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-29 16:01:47

Tak jak Wersja:

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
Author: demas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-02 07:10:59

Z ciekawości, czy coś takiego zadziała?

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

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

Wydaje się, że daje to pożądany rezultat - tablica atrybutów jest rozszerzana podczas tworzenia obiektu klasy, A wartości atrybutów zmieniają się zgodnie z oczekiwaniami:

> Foo::ATTRIBUTES
=> ['title','authors','location'] 
> Bar::ATTRIBUTES
=> ['title','authors','location', 'ISBN', 'pages'] 
 4
Author: Grant Goodale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 21:45:06

Activesupport ma metodę class_attribute w Rails edge.

 3
Author: Nick Vanderbilt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-12 02:26:14

Aby rozwinąć odpowiedź @ Nick Vanderbilt, używając active_support robisz to, co jest dokładnie krótką ręką, którą chcę dla tej funkcjonalności. Oto pełny przykład:

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

Szkoda, że tak trudno jest ruby osiągnąć to bez potrzeby posiadania biblioteki. To jedyna rzecz, której mi brakuje w Pythonie. A w moim przypadku nie przeszkadza mi zależność od klejnotu active_support.

 3
Author: Matt Connolly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-27 04:57:57

W LibraryBook zmienna @ atrybuty jest nową niezależną zmienną, zmienną instancji obiektu LibraryBook, więc jej nie zainicjalizowano i otrzymasz błąd " niezdefiniowana metoda ... for nil "
Należy go zainicjować listą libraryitem attribute przed użyciem

class LibraryBook < LibraryItem
  @attributes = LibraryItem::attributes + ['ISBN', 'pages']
end
 2
Author: andrykonchin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-02 07:56:32

To jest dla łańcuchów (cokolwiek naprawdę), a nie tablic, ale...

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"]
 0
Author: Wayne Walker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-11 22:23:58

Można to zrobić również za pomocą CINTANTS. Bez czeku.

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

class LibraryBook < LibraryItem
  ATTRIBUTES .push('ISBN', 'pages']
end
 -1
Author: Salil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-02 06:26:19