Общий сеттер для переменных экземпляра в Ruby? - PullRequest
0 голосов
/ 08 мая 2018

Я определяю класс Box в ruby, который имеет 3 переменных экземпляра: @length, @width, @height и 2 переменные класса: @@ boxcounter и @@ totalvolume.

Хотя значение @@ boxcounter может быть обновлено внутри конструктора объекта (инициализатора), обновление значения @@ totalvolume становится менее тривиальным, поскольку нам приходится пересчитывать объем данного объекта каждый раз, когда мы меняем любое из переменные экземпляра (т.е. длина, ширина или высота).

Я придумал следующий код для обработки этого варианта использования:

class Box
  @@boxcounter = 0
  @@totalvolume = 0.0

  def initialize(length, width, height)
    @length = length
    @width = width
    @height = height
    @@boxcounter += 1
    @@totalvolume += volume
  end

  def volume
    volume = @length * @width * @height
  end

  def report
    puts "# of boxes: #{@@boxcounter}"
    puts "Total volume: #{@@totalvolume}"
    puts "Average volume per box: #{@@totalvolume / @@boxcounter}"
  end

  def my_print
    p self
    puts "Length: #{@length}"
    puts "Width: #{@width}"
    puts "Height: #{@height}"
    puts
  end

  def length=(length)
    @@totalvolume -= volume
    @length = length
    @@totalvolume += volume
  end

  def width=(width)
    @@totalvolume -= volume
    @width = width
    @@totalvolume += volume
  end

  def height=(height)
    @@totalvolume -= volume
    @height = height
    @@totalvolume += volume
  end
end

Поскольку идея изучения объектов первого класса все еще остается в моей голове после изучения Scheme, я подумал: могу ли я создать универсальный установщик и использовать его для уменьшения повторения кода в каждом из перечисленных выше сеттеров? Я пробовал это, но использование eval, похоже, немного взломать:

  def update_class_state(update_instance_variable)
    @@totalvolume -= volume
    eval update_instance_variable
    @@totalvolume += volume
  end

  def length=(length)
    update_class_state("@length = #{length}")
  end

  def width=(width)
    update_class_state("@width = #{width}")
  end

  def height=(height)
    update_class_state("@height = #{height}")
  end

- Мой вопрос: это плохая практика - писать такой код? Есть ли более элегантное решение для этого подхода?

1 Ответ

0 голосов
/ 08 мая 2018

В вашем подходе нет ничего «неправильного», кроме использования eval. Вот более динамичный способ удаления дублирования без использования eval

class Box
  @@boxcounter = 0
  @@totalvolume = 0.0

  def initialize(length, width, height)
    @length = length
    @width = width
    @height = height
    @@boxcounter += 1
    @@totalvolume += volume
  end

  def volume
    volume = @length * @width * @height
  end

  def report
    puts "# of boxes: #{@@boxcounter}"
    puts "Total volume: #{@@totalvolume}"
    puts "Average volume per box: #{@@totalvolume / @@boxcounter}"
  end

  def my_print
    p self
    puts "Height: #{@height}"
  end

  # dynamically define your methods to dry up the code:
  %w[length width height].each do |method|
    define_method("#{method}=") do |arg|
      @@totalvolume -= volume
      instance_variable_set('@'+method, arg)
      @@totalvolume += volume
    end
  end
end
...