我发现自己经常对构造函数使用散列参数,尤其是在为配置编写DSL或最终用户将接触到的其他API位时.我最后做的事情如下:

class Example

    PROPERTIES = [:name, :age]

    PROPERTIES.each { |p| attr_reader p }

    def initialize(args)
        PROPERTIES.each do |p|
            self.instance_variable_set "@#{p}", args[p] if not args[p].nil?
        end
    end

end

没有比这更惯用的方法了吗?扔掉常数和符号到字符串的转换似乎特别惊人.

推荐答案

你不需要常数,但我不认为你能消除字符串中的符号:

class Example
  attr_reader :name, :age

  def initialize args
    args.each do |k,v|
      instance_variable_set("@#{k}", v) unless v.nil?
    end
  end
end
#=> nil
e1 = Example.new :name => 'foo', :age => 33
#=> #<Example:0x3f9a1c @name="foo", @age=33>
e2 = Example.new :name => 'bar'
#=> #<Example:0x3eb15c @name="bar">
e1.name
#=> "foo"
e1.age
#=> 33
e2.name
#=> "bar"
e2.age
#=> nil

顺便说一句,你可以看看(如果你还没有)Struct类生成器类,它有点类似于你正在做的,但没有哈希类型初始化(但我想这并不难使足够的生成器类).

HasProperties

为了实现胡里坎的 idea ,我得出以下结论:

module HasProperties
  attr_accessor :props
  
  def has_properties *args
    @props = args
    instance_eval { attr_reader *args }
  end

  def self.included base
    base.extend self
  end

  def initialize(args)
    args.each {|k,v|
      instance_variable_set "@#{k}", v if self.class.props.member?(k)
    } if args.is_a? Hash
  end
end

class Example
  include HasProperties
  
  has_properties :foo, :bar
  
  # you'll have to call super if you want custom constructor
  def initialize args
    super
    puts 'init example'
  end
end

e = Example.new :foo => 'asd', :bar => 23
p e.foo
#=> "asd"
p e.bar
#=> 23

由于我对元编程不太精通,我在社区维基上做了答案,所以任何人都可以自由更改实现.

struct .哈希_已初始化

根据Marc Andre的回答,这里有一个基于Struct的通用方法来创建哈希初始化类:

class Struct
  def self.hash_initialized *params
    klass = Class.new(self.new(*params))
  
    klass.class_eval do
      define_method(:initialize) do |h|
        super(*h.values_at(*params))
      end
    end
    klass
  end
end

# create class and give it a list of properties
MyClass =  struct .哈希_已初始化 :name, :age

# initialize an instance with a hash
m = MyClass.new :name => 'asd', :age => 32
p m
#=>#<struct MyClass name="asd", age=32>

Ruby相关问答推荐

如何保证散列中的对象不完全相同

将 pp 的结果(或输出到控制台的任何内容)放入字符串

Ruby 中的yield关键字有什么作用?

ruby `encode': "\xC3" 从 ASCII-8BIT 到 UTF-8 (Encoding::UndefinedConversionError)

如何使用 Ruby 删除字符串中某个字符后的子字符串?

在 Ruby 中,如何确定字符串是否不在数组中?

如何判断 Ruby 数组是否包含多个值之一?

无法从同一网络上的另一台计算机访问本地 Sinatra 服务器

全新安装 RVM 和 Ruby 2.1.1 - dyld 库/路径错误

将 2 元素数组的数组转换为哈希,其中重复键附加附加值

无法正确自动生成 Ruby DevKit 配置文件

Ruby 与计算机科学相关的 yield 特性

RSpec allow/expec vs expect/and_return

如何在数组中找到出现次数最多的项目

判断字符串是否为空的Ruby方法?

如何在 gemspec 中指定最低 Ruby 版本?

您的 Ruby 版本是 2.0.0,但您的 Gemfile 指定了 2.1.0

Ruby:是否可以在模块中定义类方法?

=== 与 == 在 Ruby 中

Ruby 类继承:什么是`<<`(双倍小于)?