Как можно использовать метапрограммирование для уменьшения избыточности в этом коде Ruby? - PullRequest
4 голосов
/ 25 июля 2009
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 ]

6 голосов
/ 26 июля 2009

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

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.

3 голосов
/ 26 июля 2009

Нечто подобное может сработать, так что вы можете декларативно определять «компоненты» (или что бы то ни было). Это излишне для такого рода примера, но вы можете использовать его, когда вам нужно определить десятки / сотни этих вещей, или вы помещаете это как часть какой-то структуры (как это делает 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 голосов
/ 25 июля 2009

Очевидно, что некоторые имена должны измениться ...

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
0 голосов
/ 28 июля 2009

Полагаю, вы, вероятно, решите этот Alreaday, в любом случае, это моя альтернатива:

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
0 голосов
/ 25 июля 2009

Можете ли вы придумать лучший пример?

Как я уже говорил в вашей предыдущей версии, здесь едва ли нужно метапрограммирование. Базовая инкапсуляция функциональности в методах работала бы.

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

0 голосов
/ 25 июля 2009

Вы уверены, что вам вообще нужно уменьшить избыточность? Это, конечно, возможно, но все, что вы делаете, только усложнит понимание кода и не обязательно будет чистым выигрышем.

...