C'è un modo per far ereditare solo una classe di una variabile da un'altra classe?

Aug 23 2020

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:

  1. Creo un "kit" animale che è un "gatto" con età "7" della famiglia "felino".
  2. C'è un'istanza di Animal_Group chiamata 'feline' con le variabili @lifespan = '17', @environment_sphere = 'terrestrial' e @breed_max = '6'
  3. '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

1 maxpleaner Aug 23 2020 at 07:48

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.

CarySwoveland Aug 24 2020 at 03:45

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 familyin Animal.newsarà '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_Groupper assegnare valori alle variabili di istanza per le istanze di Animal. In entrambi i casi il trucco è trovare l'istanza di Animal_Groupcui instance_variable @nameha un valore uguale al valore dell'argomento familyinAnimal.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_instanceche 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">