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.).
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.
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.
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
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
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']
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.
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.
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
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"]
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
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