objetos - variable de clase en ruby
¿Cómo inicializar limpiamente los atributos en Ruby con nuevo? (7)
class Foo
attr_accessor :name, :age, :email, :gender, :height
def initalize params
@name = params[:name]
@age = params[:age]
@email = params[:email]
.
.
.
end
Esto parece una manera tonta de hacerlo. ¿Cuál es una forma mejor / más idiomática de initalizar objetos en Ruby?
Ruby 1.9.3
¿Por qué no simplemente especificar explícitamente una lista real de argumentos?
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
Esta versión puede contener más líneas de código que otras, pero hace que sea más fácil aprovechar las funciones de lenguaje integradas (por ejemplo, valores predeterminados para argumentos o errores de elevación si se initialize
con arity incorrecto).
Para capitalizar la respuesta de Joshua Cheek con un poco de generalización
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: si se ha utilizado la mezcla de inicialización y necesitamos una inicialización personalizada, simplemente llamaremos a 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">
Puede simplemente iterar sobre las teclas e invocar a los instaladores. Prefiero esto porque atrapará si pasas una clave inválida.
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
Si recibe un hash como único argumento, ¿por qué no mantenerlo como una variable de instancia? Siempre que necesite un valor, llámelo desde el hash. Puede mantener corto el nombre de la variable de instancia para que pueda ser llamado fácilmente.
class Foo
attr_reader :p
def initalize p
@p = p
end
def foo
do_something_with(@p[:name])
...
end
end
Si @p[:name]
aún es demasiado largo para usted, puede guardar un proceso como variable de instancia y llamar al valor relevante como @p.(:name)
.
class Foo
attr_reader :p
def initialize p
@p = ->x{p[x]}
end
def foo
do_something_with(@p.(:name))
...
end
end
O, aún una forma alternativa es definir un método que llame al hash y aplique la clave.
class Foo
def initalize p
@p = p
end
def get key
@p[key]
end
def foo
do_something_with(get(:name))
...
end
end
Si desea establecer los valores, puede definir un método setter y, además, verificar las claves no válidas si lo desea.
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
Usar todas las claves de params no es correcto, puede definir nombres poco dispuestos. Creo que debería ser una lista bastante blanca de nombres
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''
Foo = Struct.new(:name, :age, :email, :gender, :height)
Esto es suficiente para una clase completamente funcional. Manifestación:
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
def initialize(params)
params.each do |key, value|
instance_variable_set("@#{key}", value)
end
end