Ruby习惯用法初始化类而不是对象

时间:2016-11-21 13:12:19

标签: ruby

初始化类(不是对象)的标准方法是什么?

我所做的是创建class_initialize方法并将其调用 但我是前C#程序员。还有更好的方法吗?

class Specs
  class << self
    def universal_properties
      [:hp, :engine_volume]
    end
    def compound_universal_properties
      [:hp_per_volume]
    end
    def convertible_properties
      [:weight, :torque]
    end
    def compound_convertible_properties
      [:weight_per_hp]
    end

    private
    def define_methods(type)
      define_method(type) { instance_variable_get("@#{type}") }
      define_method("#{type}=") do |value_and_unit|
        send(type).send(:set, value_and_unit)
      end
    end

    def class_initialize
      universal_properties.each { |p| define_methods(p) }
      convertible_properties.each { |p| define_methods(p) }
      compound_universal_properties.each { |p| define_methods(p) }
      compound_convertible_properties.each { |p| define_methods(p) }
    end
  end
  class_initialize

  public

  def initialize
    @weight = ConvertibleProperty.new(:weight)
    ...
  end
  ...
end

不太重要的细节:
我在第一个答案中看到这段代码令人困惑,这对于评论来说太长了。

我不只是创建attr_accessors,因为例如:weight:torque是类ConvertibleProperty并且具有imperial.valueimperial.unit,{{1}等功能}},metric.valuemetric.unit ...

我这样称这个代码:

empty?

当我输入specs = Specs.new specs.weight = 800, 'kg' specs.hp = 300 specs.torque = 210, :metric ruby​​将其转换为specs.weight = 10, 'kg'并且我不想用数组specs.weight=([10, 'kg'])替换权重时,我想在其上调用[10, 'kg']方法存储原始单位和值,并提供setmetric函数,每个函数都返回包含imperialvalue的结构。

2 个答案:

答案 0 :(得分:2)

恕我直言,最常用的方式是DSL:

class Specs
  def initialize
    instance_exec(&Proc.new) if block_given?
  end
  def weight!(*args)
    weight = ...
  end
  ...
end

specs = Specs.new do 
  weight! 800, 'kg'
  hp! 300
  torque! 210, :metric
end

其他方式是指定适当的访问者:

def torque=(*args)
  # 210, :metric
  @torque = ConvertibleProperty.new(...)
end

如果变量数量很大,可能需要自动创建访问器:

PROPERTIES = {
  'UniversalProperty': [:hp, :engine_volume],
  'CompoundUniversalProperty': [:hp_per_volume],
  'ConvertibleProperty': [:weight, :torque],
  'CompoundConvertibleProperty': [:weight_per_hp]
}.freeze

PROPERTIES.each do |type, *props|
  props.each do |prop|
    attr_reader prop
    define_method "#{prop}=" do |*args|
      self.instance_variable_set(:"@#{prop}", Kernel.const_get(type).new(*args))
    end
  end
end

答案 1 :(得分:1)

惯用法,您不会创建以这种方式响应的分配方法,而且这里对元编程没有多大价值。我会首先用简单的术语写出来,然后如果管理你的不同类型变得很麻烦,那么可能稍后重构。

以下是惯用法:

class Specs
  def weight
    @weight ||= ConvertibleProperty.new(:weight)
  end

  def torque
    @torque ||= ConvertibleProperty.new(:torque)
  end

  # [..]
end

使用

specs = Specs.new
specs.weight.set(800, 'kg')
specs.torque.set(210, :metric)