Ответ 1
def initialize(params)
params.each do |key, value|
instance_variable_set("@#{key}", value)
end
end
class Foo
attr_accessor :name, :age, :email, :gender, :height
def initalize params
@name = params[:name]
@age = params[:age]
@email = params[:email]
.
.
.
end
Это кажется глупым способом сделать это. Что является лучшим/более идиоматическим способом инициализации объектов в Ruby?
Ruby 1.9.3
def initialize(params)
params.each do |key, value|
instance_variable_set("@#{key}", value)
end
end
Вы можете просто перебирать ключи и вызывать сеттеры. Я предпочитаю это, потому что он поймает, если вы передадите недопустимый ключ.
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
Чтобы извлечь выгоду из ответа Джошуа Чика с небольшим обобщением
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>
NB Если используется инициализационный микс, и нам нужна настраиваемая инициализация, мы просто вызываем 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">
Почему бы просто не указать явный список аргументов?
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
вызывается с неправильной арностью).
Foo = Struct.new(:name, :age, :email, :gender, :height)
Этого достаточно для полноценного класса. Демо-ролик:
p Foo.class # Class
employee = Foo.new("smith", 29, "[email protected]", "m", 1.75) #create an instance
p employee.class # Foo
p employee.methods.sort # huge list which includes name, name=, age, age= etc
Использование всех ключей из параметров неверно, вы можете определить неопытные имена. Я думаю, что это должен быть белый список имен
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'
Если вы принимаете хэш в качестве единственного аргумента, почему бы просто не сохранить его как переменную экземпляра? Всякий раз, когда вам нужно значение, назовите его из хэша. Вы можете сохранить имя переменной экземпляра коротким, чтобы его можно было легко вызвать.
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
Если вы хотите установить значения, вы можете определить метод сеттера и, если хотите, проверить недействительные ключи.
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