Ruby дочерний класс обновляет переменную родительского класса - PullRequest
0 голосов
/ 05 февраля 2020

Я пытаюсь обновить переменную родительского класса из дочернего класса. Это то, что у меня есть

class Dog
  def initialize
    @breed = []
  end

  def show
    puts "#{@first_name} of breed #{@breed}" ### <- Changed
  end
end

class Lab < Dog

  attr_reader :first_name, :i
  def initialize
    super ### <- Added
    @first_name = "good dog"
    @i = 1
  end
  def add
    @breed << @i
    @i += 1
  end
end

Как видите, у меня есть переменная @breed в родительском классе, к которой имеет доступ дочерний класс. Но когда я обновляю переменную @breed из дочернего класса и вызываю ту же переменную из родительского класса, она не показывает значения обновления. Как я могу это сделать?

irb(main):172:0> d = Dog.new
=> #<Dog:0x00005607908130d8 @breed=[]>
irb(main):173:0> l = Lab.new
=> #<Lab:0x000056079081add8 @breed=[], @first_name="good dog", @i=1>
irb(main):174:0> d.show
 of breed []

irb(main):175:0> l.show
good dog of breed []

irb(main):176:0> l.add
=> 2
irb(main):177:0> l.show # inherited variable got updated
good dog of breed [1]

irb(main):178:0> d.show # parent array did not get updated?
 of breed []

irb(main):179:0> 

Вызов d.show возвращает пустой массив, но вызов l.show возвращает заполненный массив. Я хотел бы, чтобы мой родительский класс также возвращал заполненный массив. Это возможно? Чего мне не хватает.

Ответы [ 3 ]

1 голос
/ 05 февраля 2020

Нет, это невозможно, по крайней мере, без некоторого (нежелательного) взлома.

Вам не хватает различия между классами и экземплярами . Может быть иерархия классов, как вы создали с помощью Dog и Lab, но экземпляры, которые вы создаете из этих классов, не имеют иерархических отношений. Переменные (экземпляра), такие как @breed, являются частными для каждого экземпляра, и каждый экземпляр имеет свои собственные копии.

Другими словами: " parent array" нет, только массив, который был объявлен в родительском классе.

Похоже, вы хотите коллекцию Dog экземпляров. В правильном месте будет переменная вне класса Dog.

class Dog 
  attr_reader :first_name

  def initialize(name)
    @first_name = name
  end

  def show
    puts "#{first_name} of breed #{self.class.name}"
  end
end

class Lab < Dog
  def initialize
    super('good dog')
  end
end

class Schnauzer < Dog
end

class BlackLab < Lab
end

Обратите внимание, что я переместил first_name с Lab на Dog, потому что Dog#show уже использует его, и нет смысла объявлять его в подклассе.

Вы можете использовать эту иерархию классов для сбора нескольких экземпляров в массиве, который хранится вне иерархии классов:

breeds = []
breeds << Dog.new('some dog')
breeds << Lab.new
breeds << Dog.new('another dog')
breeds << Schnauzer.new('yet another')
breeds << BlackLab.new

breeds.each do |dog_instance|
  dog_instance.show
end

Это приводит к следующему выводу:

some dog of breed Dog
good dog of breed Lab
another dog of breed Dog
yet another of breed Schnauzer
good dog of breed BlackLab
1 голос
/ 05 февраля 2020
class Dog
  def initialize
    puts "self in Dog#initialize = #{self}" 
    @breed = []
  end
  def show
    puts "#{@first_name} of breed #{@breed}"
  end
end

class Lab < Dog
  attr_reader :first_name, :i
  def initialize
    super
    @first_name = "good dog"
    @i = 1
  end
  def add
    @breed << @i
    @i += 1
  end
end

Давайте создадим экземпляр Lab и рассмотрим его переменные экземпляра:

sue = Lab.new
  #=> #<Lab:0x000059195710eec8 @breed=[], @first_name="good dog", @i=1>
  # displays: self in Dog#initialize = #<Lab:0x000059195710eec8>
sue.instance_variables
  #=> [:@breed, :@first_name, :@i]
sue.first_name"
  #=> "good dog" 
sue.i
  #=> 1 
sue.instance_variable_get(:@breed)
  #=> []  

Обратите внимание, что мне пришлось использовать Object # instance_variable_get , чтобы получить значение переменной экземпляра sue @breed, поскольку для этой переменной экземпляра не было создано средство получения.

Вы говорите, что wi sh to "update переменная родительского класса из дочернего класса ". Во-первых, родительский класс Dog не имеет переменных класса или переменных экземпляра (иногда их называют переменные экземпляра класса ). Я предполагаю, что вы имеете в виду переменную экземпляра @breed, связанную с каждым экземпляром Dog. На самом деле, похоже, что вряд ли кто-то создаст экземпляр Dog; что Dog было создано только для определения методов и переменных экземпляра, которые должны использоваться подклассами Dog. Тем не менее, давайте создадим экземпляр Dog:

bob = Dog.new
  #=> #<Dog:0x0000591956ffd4a8 @breed=[]> 
  # displays: self in Dog#initialize = #<Dog:0x0000591956ffd4a8>
bob.instance_variables
  #=> [:@breed] 
bob.instance_variable_get(:@breed)
  #=> [] 

Давайте также изменим значение @breed для этого экземпляра:

bob.instance_variable_get(:@breed) << "pug"
  #=> ["pug"] 

Теперь давайте добавим метод get_iv Lab, чтобы получить значение переменной экземпляра @breed экземпляра Dog. Подумайте об информации, которая должна быть передана этому методу. Нужно знать, какой экземпляр Dog нас интересует (поскольку разные экземпляры могут очень хорошо иметь разные значения для своих переменных экземпляра), а для этого экземпляра - какая переменная экземпляра нас интересует. (Здесь есть только один, но давайте сделаем так, чтобы мы могли добавлять переменные экземпляра к экземплярам Dog.

class Lab
  def get_iv(instance, instance_variable)
    instance.public_send(:instance_variable_get, instance_variable)
  end
end

См. Object # public_send . Теперь мы можем написать:

sue.get_iv(bob, :@breed)
  #=> ["pug"]
sue.get_iv(bob, :@breed) << "collie"
  #=> ["pug", "collie"]    

Конечно же, мы выполнили задачу:

bob.instance_variable_get(:@breed)
  #=> ["pug", "collie"]    

Но подождите! Теперь давайте создадим совершенно не связанный класс и его экземпляр.

class Bird
  def initialize
    @type = ["canary"]
  end
end

tweetie = Bird.new
  #=> #<Bird:0x0000591956ffee98 @type=["canary"]> 

Далее, давайте sue получим и изменим значение * Переменная экземпляра 1059 * @type:

sue.get_iv(tweetie, :@type)
  #=> ["canary"] 
sue.get_iv(tweetie, :@type) << "robin"
  #=> ["canary", "robin"] 

и подтверждение того, что они были изменены:

tweetie.instance_variable_get(:@breed)
  #=> ["canary", "robin"] 

Дело в том, что метод экземпляра мы должны добавить к Lab требуется две части информации:

  • экземпляр, значение переменной экземпляра которого необходимо получить; и
  • имя переменной экземпляра.

Это не имеет ничего общего с тем фактом, что Lab является подклассом Dog! Следовательно, получение или установка значения переменной экземпляра экземпляра суперкласса Lab ничем не отличается от получения или установки значения переменной экземпляра любого другого класса .

1 голос
/ 05 февраля 2020

d и l в вашем примере - это абсолютно разные объекты , не имеющие ничего общего. Это не то, как работает наследование.

Был бы краткий пример использования переменной экземпляра из родительского класса.

class Dog
  def initialize
    @breed = []
  end
  def add
    puts "Dog: #{@breed}"
  end
end

class Lab < Dog
  def initialize
    super ### <- @breed is now declared
  end

  def add
    puts "Lab 1: #{@breed}"
    @breed << :ok
    super
    puts "Lab 2: #{@breed}"
  end
end

Lab.new.add
#⇒ Lab 1: []
#⇒ Dog: [:ok]
#⇒ Lab 2: [:ok]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...