在红宝石中,我经常发现自己写了以下内容:
class Foo
def initialize(bar, baz)
@bar = bar
@baz = baz
end
<< more stuff >>
end
甚至
class Foo
attr_accessor :bar, :baz
def initialize(bar, baz)
@bar = bar
@baz = baz
end
<< more stuff >>
end
我总是希望尽可能地减少样板 - 所以有没有更惯用的方法在ruby中创建对象?
答案 0 :(得分:11)
一个选项是您可以从Struct继承您的类定义:
class Foo < Struct.new(:bar, :baz)
# << more stuff >>
end
f = Foo.new("bar value","baz value")
f.bar #=> "bar value"
f.baz #=> "baz value"
答案 1 :(得分:10)
我问a duplicate question,并在那里建议我自己的答案,期待更好的答案,但是没有出现令人满意的答案。我会张贴自己的。
根据attr_accessor
,attr_reader
,attr_writer
方法的精神定义类似以下的类方法。
class Class
def attr_constructor *vars
define_method("initialize") do |*vals|
vars.zip(vals){|var, val| instance_variable_set("@#{var}", val)}
end
end
end
然后,您可以像这样使用它:
class Foo
attr_constructor :foo, :bar, :buz
end
p Foo.new('a', 'b', 'c') # => #<Foo:0x93f3e4c @foo="a", @bar="b", @buz="c">
p Foo.new('a', 'b', 'c', 'd') # => #<Foo:0x93f3e4d @foo="a", @bar="b", @buz="c">
p Foo.new('a', 'b') # => #<Foo:0x93f3e4e @foo="a", @bar="b", @buz=nil>
答案 2 :(得分:2)
Struct
对象是几乎可以完成你想要的类。唯一的区别是,initialize方法的nil作为所有参数的默认值。你像这样使用它
A= Struct.new(:a, :b, :c)
或
class A < Struc.new(:a, :b, :c)
end
Struct
有一个很大的缺点。你不能从另一个类继承。
您可以编写自己的方法来指定属性
def attributes(*attr)
self.class_eval do
attr.each { |a| attr_accessor a }
class_variable_set(:@@attributes, attr)
def self.get_attributes
class_variable_get(:@@attributes)
end
def initialize(*vars)
attr= self.class.get_attributes
raise ArgumentError unless vars.size == attr.size
attr.each_with_index { |a, ind| send(:"#{a}=", vars[ind]) }
super()
end
end
end
class A
end
class B < A
attributes :a, :b, :c
end
现在你的类可以从其他类继承。这里唯一的缺点是,你无法获得初始化的参数数量。这与Struct
相同。
B.method(:initialize).arity # => -1
答案 3 :(得分:2)
您可以使用Virtus,我认为这不是惯用的方法,但它会为您完成所有锅炉板。
require 'Virtus'
class Foo
include 'Virtus'
attribute :bar, Object
attribute :baz, Object
end
然后你可以做像
这样的事情foo = Foo.new(:bar => "bar")
foo.bar # => bar
如果您不想将哈希传递给初始化程序,请添加:
def initialize(bar, baz)
super(:bar => bar, :baz => baz)
end
如果你认为它不够干,你也可以
def initialize(*args)
super(self.class.attributes.map(&:name).zip(args)])
end
答案 4 :(得分:1)
我有时会这样做
@bar, @baz = bar, baz
仍然是样板,但它只占用一行。
我想你也可以做
["bar", "baz"].each do |variable_name|
instance_variable_set(:"@#{variable_name}", eval(variable_name))
end
(我相信这样做的危险性较小,请注意)
https://bugs.ruby-lang.org/issues/5825是一个使样板文件更简洁的提案。
答案 5 :(得分:-1)
您可以将对象用作参数。
class Foo
attr_accessor :param
def initialize(p)
@param = p
end
end
f = Foo.new
f.param.bar = 1
f.param.bax = 2
在这种情况下,这不会节省太多行,但是如果你的类必须处理大量的参数,那么它就会存在。如果要将@param var保密,也可以实现set_param和get_param方法。