【问题标题】:How could metaprogramming be used to reduce redundancy in this Ruby code?如何使用元编程来减少此 Ruby 代码中的冗余?
【发布时间】:2009-07-25 18:15:39
【问题描述】:
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 中使用元编程来减少一些重复性。

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

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

    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 方法的主体。

    【讨论】:

      【解决方案2】:

      这样的事情可能会起作用,因此您可以以声明方式定义“组件”(或任何它们)。对于这类示例来说,这有点过头了,但是当您需要定义数十/数百个这样的东西时,您可以使用它,或者将它作为某些框架的一部分(就像 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
      

      【讨论】:

        【解决方案3】:

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

        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
        

        【讨论】:

          【解决方案4】:

          您确定需要减少此处的冗余吗?这当然是有可能的,但你所做的任何事情都只会让代码更难理解,而且不一定会赢。

          【讨论】:

            【解决方案5】:

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

            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
            

            【讨论】:

              【解决方案6】:

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

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

              人们给出的任何示例都是人为的,并不能真正代表元编程的现实世界使用情况。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-05-26
                • 2010-11-09
                • 1970-01-01
                • 2021-12-30
                • 2018-02-28
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多