在ruby中创建惯用对象
在ruby中,我经常发现自己编写以下内容:
class Foo def initialize(bar, baz) @bar = bar @baz = baz end <> end
甚至
class Foo attr_accessor :bar, :baz def initialize(bar, baz) @bar = bar @baz = baz end <> end
我总是希望尽可能地减少样板 – 所以是否有更惯用的方式在ruby中创建对象?
一个选项是您可以从Structinheritance您的类定义:
class Foo < Struct.new(:bar, :baz) # << more stuff >> end f = Foo.new("bar value","baz value") f.bar #=> "bar value" f.baz #=> "baz value"
我问了一个重复的问题 ,并在那里提出了我自己的答案,希望有一个更好的答案,但是没有出现令人满意的问题。 我会张贴自己的。
根据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') # => # p Foo.new('a', 'b', 'c', 'd') # => # p Foo.new('a', 'b') # => #
结构
Struct
对象是几乎可以满足您需求的类。 唯一的区别是,initialize方法的nil作为所有参数的默认值。 你这样使用它
A= Struct.new(:a, :b, :c)
要么
class A < Struc.new(:a, :b, :c) end
Struct
有一个很大的缺点。 你不能从另一个类inheritance。
编写自己的属性说明符
您可以编写自己的方法来指定属性
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
现在你的类可以inheritance其他类。 这里唯一的缺点是,你无法获得初始化的参数数量。 这与Struct
相同。
B.method(:initialize).arity # => -1
您可以使用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
我有时会这样做
@bar, @baz = bar, baz
仍然是样板,但它只占用一条线。
我想你也可以这样做
["bar", "baz"].each do |variable_name| instance_variable_set(:"@#{variable_name}", eval(variable_name)) end
(我相信这样做的危险性较小,请注意)
您可以使用对象作为参数。
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方法。