C'è un modo per far ereditare solo una classe di una variabile da un'altra classe?
Sono un principiante e forse mi sono perso qualcosa nelle mie ricerche, ma ho esaminato le documentazioni e le altre domande qui e non ho trovato la risposta alla mia domanda, quindi la metterò qui e scusami se è già spiegato altrove. Ho due classi in ruby, una chiamata Animal e l'altra Animal_Group. All'interno dell'inizializzazione di Animal, ho una variabile chiamata @family, e vorrei confrontare questa variabile con le istanze già esistenti di Animal_Group e, se c'è una corrispondenza, volevo che ereditasse le definizioni (compresi i valori per Animal_Group class intialize variabili stesse) della classe Animal_Group e dell'istanza di essa.
Giusto per illustrare, sarebbe qualcosa del genere:
class Animal
def initialize(ident, species, family, age)
@ident = ident
@species = species
@family = family
if family.class == Animal_Group
super(*args)
end
@age = age
end
end
Quindi risulterebbe su qualcosa del genere:
- Creo un "kit" animale che è un "gatto" con età "7" della famiglia "felino".
- C'è un'istanza di Animal_Group chiamata 'feline' con le variabili @lifespan = '17', @environment_sphere = 'terrestrial' e @breed_max = '6'
- 'kit' avrebbe le variabili e i suoi valori come segue:
@ident = 'kit' @species = 'cat' @family = 'feline' @age = '7' @lifespan = '17' @environment_sphere = 'terrestrial' @breed_max = '6'
C'è un modo per farlo o l'unico modo è trasformare tutto in nuove classi?
Risposte
Fondamentalmente, senza metaprogrammazione, questo non è possibile. Anche se potrei mostrare come farlo con la metaprogrammazione, lo lascerò come esercizio al lettore, perché quando si arriva a questo, è meglio mantenere le cose semplici, e c'è sicuramente un modo più semplice per farlo.
Ad ogni modo, quello che consiglierei è un sistema come questo:
class AnimalGroup
attr_reader :lifespan, :environment_sphere, :breed_max, :age
end
class Feline < AnimalGroup
def initialize
@lifespan = '17'
@environment_sphere = 'terrestrial'
@breed_max = '6'
end
end
class Cat < Feline
def initialize(age)
@age = age
super
end
end
Capisco che questo potrebbe sembrare un po 'meno flessibile del codice che volevi scrivere. Invece di memorizzare il "tipo" del gruppo animale come variabile di istanza dinamica, lo stiamo fondamentalmente codificando con la definizione della classe. Ma si spera che tu possa vedere come questo approccio sia più semplice.
Supponiamo
class Animal_Group
attr_reader :name, :lifespan, :breed_mix
def initialize(name, lifespan, breed_mix)
@name = name
@lifespan = lifespan
@breed_mix = breed_mix
end
end
Animal_Group.new("Boots", 18, "Siamese")
#=> #<Animal_Group:0x000056fa3111a878 @name="Boots", @lifespan=18,
# @breed_mix="Siamese">
Animal_Group.new("Hank", 14, "Tabby")
#=> #<Animal_Group:0x000056fa3114f618 @name="Hank", @lifespan=14,
# @breed_mix="Tabby">
Presumo che un'istanza dell'argomento family
in Animal.new
sarà 'Boots'
o 'Hank'
, come in
Animal.new('kit', 'Hank', 7)
Se ho frainteso la domanda non c'è motivo di leggere oltre.
Esistono due approcci che possono essere adottati per utilizzare le informazioni dalle istanze di Animal_Group
per assegnare valori alle variabili di istanza per le istanze di Animal
. In entrambi i casi il trucco è trovare l'istanza di Animal_Group
cui instance_variable @name
ha un valore uguale al valore dell'argomento family
inAnimal.new
Usa ObjectSpace :: each_object
class Animal
def initialize(ident, family, age)
@ident = ident
@family = family
@age = age
family_instance = ObjectSpace.each_object(Object).find do |o|
o.class == Animal_Group && o.name == family
end
# raise exception here if family_instance is nil
@lifespan = family_instance.lifespan
@breed_mix = family_instance.breed_mix
end
end
Animal.new('kit', 'Hank', 7)
#=> #<Animal:0x000056fa31211f10 @ident="kit", @family="Hank",
# @age=7, @lifespan=14, @breed_mix="Tabby">
Animal.new('kat', 'Boots', 4)
#=> #<Animal:0x000056fa31174918 @ident="kat", @family="Boots",
# @age=4, @lifespan=18, @breed_mix="Siamese">
Mantieni un elenco di istanze di Animal_Group
class Animal_Group
@name_to_instance = {}
singleton_class.public_send(:attr_reader, :name_to_instance)
attr_reader :name, :lifespan, :breed_mix
def initialize(name, lifespan, breed_mix)
@name = name
@lifespan = lifespan
@breed_mix = breed_mix
self.class.name_to_instance[name] = self
end
end
Nota che
singleton_class.public_send(:attr_reader, :name_to_instance)
(dove self #=> Animal_Group
) crea una funzione di accesso in lettura per la variabile di istanza della classe @name_to_instance
che contiene un hash che mappa i nomi delle istanze alle istanze.
Animal_Group.new("Boots", 18, "Siamese")
#=> #<Animal_Group:0x000056fa311b6110 @name="Boots", @lifespan=18,
# @breed_mix="Siamese">
Animal_Group.new("Hank", 14, "Tabby")
#=> #<Animal_Group:0x000056fa311e9998 @name="Hank", @lifespan=14,
# @breed_mix="Tabby">
Nota
Animal_Group.name_to_instance
#=> {"Boots"=>#<Animal_Group:0x000056fa311b6110 @name="Boots",
# @lifespan=18, @breed_mix="Siamese">,
# "Hank"=>#<Animal_Group:0x000056fa311e9998 @name="Hank",
# @lifespan=14, @breed_mix="Tabby">}
class Animal
def initialize(ident, family, age)
@ident = ident
@family = family
@age = age
family_instance = Animal_Group.name_to_instance[family]
# raise exception here if family_instance is nil
@lifespan = family_instance.lifespan
@breed_mix = family_instance.breed_mix
end
end
Animal.new('kit', 'Hank', 7)
#=> #<Animal:0x000056fa311fd9c0 @ident="kit", @family="Hank",
# @age=7, @lifespan=14, @breed_mix="Tabby">
Animal.new('kat', 'Boots', 4)
#=> #<Animal:0x000056fa31212cf8 @ident="kat", @family="Boots",
# @age=4, @lifespan=18, @breed_mix="Siamese">