32
class Foo
  attr_accessor :name, :age, :email, :gender, :height

  def initalize params
    @name = params[:name]
    @age = params[:age]
    @email = params[:email]
    .
    .
    .
  end

这似乎是一种愚蠢的做法。在 Ruby 中初始化对象的更好/更惯用的方法是什么?

红宝石 1.9.3

4

7 回答 7

42

您可以只遍历键并调用设置器。我更喜欢这个,因为如果您传递无效的密钥,它会捕获。

class Foo
  attr_accessor :name, :age, :email, :gender, :height

  def initialize params = {}
    params.each { |key, value| send "#{key}=", value }
  end
end

foo = Foo.new name: 'Josh', age: 456
foo.name  # => "Josh"
foo.age   # => 456
foo.email # => nil
于 2012-10-06T19:20:17.743 回答
17
def initialize(params)
  params.each do |key, value|
    instance_variable_set("@#{key}", value)
  end
end
于 2012-10-06T19:20:03.113 回答
15

通过一些概括来利用 Joshua Cheek 的回答

module Initializable
  def initialize(params = {})
    params.each do |key, value|
      setter = "#{key}="
      send(setter, value) if respond_to?(setter.to_sym, false)
    end
  end
end

class Foo
  include Initializable

  attr_accessor :name, :age, :email, :gender, :height
end

Foo.new name: 'Josh', age: 456
=> #<Foo:0x007fdeac02ecb0 @name="Josh", @age=456>

注意如果已经使用了初始化混合并且我们需要自定义初始化,我们只需调用 super:

class Foo
  include Initializable

  attr_accessor :name, :age, :email, :gender, :height, :handler

  def initialize(*)
    super

    self.handler = "#{self.name} #{self.age}"
  end
end

Foo.new name: 'Josh', age: 45
=> #<Foo:0x007fe94c0446f0 @name="Josh", @age=45, @handler="Josh 45"> 
于 2015-04-14T21:59:32.620 回答
7
Foo = Struct.new(:name, :age, :email, :gender, :height)

这对于一个功能齐全的班级来说已经足够了。演示:

p Foo.class # Class

employee = Foo.new("smith", 29, "smith@foo.com", "m", 1.75) #create an instance
p employee.class # Foo
p employee.methods.sort # huge list which includes name, name=, age, age= etc
于 2012-10-06T20:31:06.247 回答
4

为什么不直接明确指定一个实际的参数列表?

class Foo
  attr_accessor :name, :age, :email, :gender, :height

  def initialize(name, age, email, gender, height)
    @name = name
    @age = age
    @email = email
    @gender = gender
    @height = height
  end
end

这个版本可能比其他版本有更多的代码行,但它更容易利用内置的语言特性(例如,参数的默认值或如果initialize以不正确的数量调用则引发错误)。

于 2012-10-06T20:19:28.693 回答
2

使用 params 中的所有键是不正确的,您可以定义不愿意的名称。我认为这应该是一个名字的白名单

class Foo
   @@attributes = [:name, :age, :email, :gender, :height]  

   @@attributes.each do |attr|
     class_eval { attr_accessor "#{attr}" }
   end  

   def initialize params
     @@attributes.each do |attr|
       instance_variable_set("@#{attr}", params[attr]) if params[attr]
     end  
   end
end

Foo.new({:name => 'test'}).name #=> 'test'
于 2012-10-06T19:39:46.927 回答
1

如果您收到一个散列作为唯一参数,为什么不将其保留为实例变量?每当您需要一个值时,请从哈希中调用它。您可以保持实例变量名称简短,以便可以轻松调用它。

class Foo
  attr_reader :p
  def initalize p
    @p = p
  end
  def foo
    do_something_with(@p[:name])
    ...
  end
end

如果@p[:name]对您来说仍然太长,那么您可以将 proc 保存为实例变量,并调用相关值,例如@p.(:name).

class Foo
  attr_reader :p
  def initialize p
    @p = ->x{p[x]}
  end
  def foo
    do_something_with(@p.(:name))
    ...
  end
end

或者,另一种方法是定义一个调用散列并应用密钥的方法。

class Foo
  def initalize p
    @p = p
  end
  def get key
    @p[key]
  end
  def foo
    do_something_with(get(:name))
    ...
  end
end

如果要设置值,您可以定义一个 setter 方法,并根据需要进一步检查无效键。

class Foo
  Keys = [:name, :age, :email, :gender, :height]
  def initalize p
    raise "Invalid key in argument" unless (p.keys - Keys).empty?
    @p = p
  end
  def set key, value
    raise "Invalid key" unless Keys.key?(key)
    @p[key] = value
  end
  def get key
    @p[key]
  end
  def foo
    do_something_with(get(:name))
    ...
  end
end
于 2012-10-06T19:29:41.993 回答