【问题标题】:Generic setter for instance variables in Ruby?Ruby中实例变量的通用设置器?
【发布时间】:2018-05-07 23:26:26
【问题描述】:

我在 ruby​​ 中定义了一个 Box 类,它有 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 之后,头等对象的想法仍然存在,我想知道,我是否可以创建一个通用的 setter 并使用它来减少上面列出的每个 setter 中的代码重复?我试过这个,但使用 eval 似乎有点 hack:

  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

– 我的问题:编写这样的代码是一种不好的做法吗?这种方法有更优雅的解决方案吗?

【问题讨论】:

标签: ruby eval


【解决方案1】:

除了使用 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

【讨论】:

  • 感谢您的回复,这是一个非常有趣的方法。
  • @wintermute 没问题。每当您看到除了说出变量名称之外的相同代码时,您几乎总是可以使用某种动态 ruby​​ 方法和字符串插值来干掉事情。但是,这种方法的一个缺点是,如果您有大量代码库并且正在搜索方法定义,则可能无法通过全文搜索找到它。在这些情况下,可能值得添加带有生成方法名称的 cmets。
猜你喜欢
  • 2010-10-24
  • 2018-03-01
  • 1970-01-01
  • 2014-07-08
  • 2018-07-18
  • 2012-01-07
  • 1970-01-01
  • 2014-09-21
  • 1970-01-01
相关资源
最近更新 更多