2009-11-22 52 views
14

在Ruby中,我经常发现自己写如下:成语对象创建

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

另见:http:// stacko veroo.com/questions/12763016/how-to-cleanly-initialize-attributes-in-ruby-with-new – 2014-01-07 23:49:09

回答

11

一种选择是,你可以从结构继承类定义:

class Foo < Struct.new(:bar, :baz) 
    # << more stuff >> 
end 

f = Foo.new("bar value","baz value") 
f.bar #=> "bar value" 
f.baz #=> "baz value" 
+3

而不是子类化,你也可以做'Foo = Struct.new(:bar,:baz)do ...结束' – sepp2k 2009-11-22 13:38:15

+0

你能举一个例子,sepp2k吗? – Geo 2009-11-22 15:56:56

+1

有趣......但如果你想让Foo子类别的东西呢? – grifaton 2010-01-19 23:39:47

-1

你可以使用一个对象作为PARAM。

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 private,你也可以实现一个set_param和get_param方法。

2

结构

Struct对象是你想要什么几乎做哪个班。唯一的区别是,初始化方法对于所有参数都没有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 
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是使样板更简洁的建议。

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> 
+0

有趣,我喜欢它! – grifaton 2011-10-24 09:56:05

+0

@ grifaton谢谢。 – sawa 2011-10-24 14:18:02

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