如何使用元编程来减少Ruby代码中的冗余?

时间:2009-07-25 18:15:39

标签: ruby metaprogramming

class Device
  def initialize(device_id, data_resource)
    @id = device_id
    @data_resource = data_resource
  end

  def display_device
    mode = @data_resource.get_display_device_mode(@id)
    presets = @data_resource.get_display_device_presets(@id)
    summary = "display_device: #{mode} ($#{presets})"
    return "* #{summary}" if presets == "XTC909"
    summary
  end

  def chip
    mode = @data_resource.get_chip_mode(@id)
    presets = @data_resource.get_chip_presets(@id)
    summary = "chip: #{mode} ($#{presets})"
    return "* #{summary}" if presets == "XTC909"
    summary
  end

  def input_device
    mode = @data_resource.get_input_device_mode(@id)
    presets = @data_resource.get_input_device_presets(@id)
    summary = "input_device: #{mode} ($#{presets})"
    return "* #{summary}" if presets == "XTC909"
    summary
  end

end

从上面的代码中可以看出,这些方法中存在相当多的冗余。 无论元编程是否是减少此冗余的最佳方法,我希望学习如何在Ruby中使用元编程来减少某些重复性,如果有人可以提供一些建议的话。

6 个答案:

答案 0 :(得分:6)

这是一个使用元编程的版本,虽然我也会通过将它放在它所属的方法中来删除重复。

class Device
  def initialize(device_id, data_resource)
    @id = device_id
    @data_resource = data_resource
  end

  def resource_summary(resource_name)
    mode = @data_resource.send("get_#{resource_name}_mode", @id)
    presets = @data_resource.send("get_#{resource_name}_presets", @id)
    summary = "#{resource_name}: #{mode} ($#{presets})"
    return "* #{summary}" if presets == "XTC909"
    summary
  end

  def self.resource_accessor(*names)
    names.each {|resource| define_method(resource) {resource_summary resource}}
  end

  resource_accessor :display_device, :chip, :input_device
end

如果您真的不想为该功能制作方法,则只需将resource_summary方法调用替换为resource_summary方法的正文。

答案 1 :(得分:3)

这样的东西可以工作,所以你可以声明性地定义'组件'(或者它们是什么)。对于这类示例来说这是过度的,但是当你需要定义几十个/几百个这样的东西时,你可以使用它,或者你把它作为某个框架的一部分(比如rails)。

component类级别方法通常会存在于包含在类中的其他模块中,而不是像这样将其内联声明为内联语。

class Device

  class << self 
    def component(component_name)
      define_method(component_name) do
        mode = @data_resource.send("get_#{component_name}_mode", @id)
        presets = @data_resource.send("get_#{component_name}_presets", @id)
        summary = "#{component_name} : #{mode} ($#{presets})"
        presets == "XTC909" ? "* #{summary}" : summary
      end
    end
  end

  component :display_device
  component :chip
  component :input_device

  def initialize(device_id, data_resource)
    @id = device_id
    @data_resource = data_resource
  end
end

您可以使用以下内容驾驶它:

class DataResource
  def method_missing(method, *args)
    # puts "called #{method} with:#{args.inspect}"
    "#{method}-#{args.join(':')}"
  end
end

device = Device.new("ID123", DataResource.new)
puts device.display_device
puts device.chip
puts device.input_device

答案 2 :(得分:2)

显然,有些名字应该改变......

def display_device
  i_heart_meta_programming("display_device")
end

def chip
  i_heart_meta_programming("chip")
end

def input_device
  i_heart_meta_programming("input_device")
end

def i_heart_meta_programming(what_to_get)
  mode = @data_resource.send("get_#{what_to_get}_mode", @id)
  mode = @data_resource.send("get_#{what_to_get}_presets", @id)
  summary = "#{what_to_get}: #{mode} ($#{presets})"
  return "* #{summary}" if presets == "XTC909"
  summary
end

答案 3 :(得分:0)

您确定需要在这里减少冗余吗?这当然是可能的,但是你做的任何事情都会让代码更难理解,也不一定是净胜利。

答案 4 :(得分:0)

我想你可能解决了这个问题,无论如何这是我的选择:

class Device
  def initialize(device_id, data_resource)
    @id,@data_resource = device_id, data_resource
  end

  %w{display_device chip input_device}.each do |met|
    define_method met do  
      mode = @data_resource.send("get_#{met}_mode", @id)
      presets = @data_resource.send("get_#{met}_presets",@id)
      summary = "#{met}: #{mode} ($#{presets})"
      return "* #{summary}" if presets == "XTC909"
      summary
    end
  end
end

答案 5 :(得分:-1)

你能想出一个更好的例子吗?

正如我之前所说的那样,这里几乎不需要元编程。方法中功能的基本封装可行。

人们给出的任何例子都是人为的,并不能真正代表元编程的现实用法。