2011-10-21 66 views
6

可能重複:
Idiomatic object creation in ruby談到構造函數的參數爲​​實例變量

有很多occaisions時,我有一個initialize方法,看起來像這樣:

class Foo 
    def initialize bar, buz, ... 
     @bar, @buz, ... = bar, buz, ... 
    end 
end 

是有一種方法可以通過一個簡單的命令來實現,如:

class Foo 
    attr_constructor :bar, :buz, ... 
end 

其中符號表示實例變量的名稱(用attr_accessorattr_readerattr_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

你想在創建過程中初始化屬性嗎? –

+1

我認爲這個問題以前曾被問過,但我還沒有找到任何確切的重複。 –

+0

@AndrewGrimm感謝您提及重複的問題。我會在那裏看到答案。我會投票自己關閉這個。 – sawa

回答

1

這對你有用嗎?

class Foo 
    def initialize(hash) 
     hash.each { |k,v| instance_variable_set("@#{k}", v) } 
    end 
end 
+0

這不完全是我想要的。我想要一個類方法在類體中使用一次,它控制'initialize'的行爲。 – sawa

1

我會使用OpenStruct

require 'ostruct' 

class Foo < OpenStruct 
end 

f = Foo.new(:bar => "baz") 
f.bar 
#=> "baz" 

編輯: OK啊,對不起,誤解你了。怎麼樣只是:

class Foo 
    def initialize(*args) 
    @baz, @buz = args 
    end 
end 
+0

在您的代碼中,您需要在構造時指定實例變量的名稱,這比僅在'initialize'方法中定義該名稱更糟糕。 – sawa

+0

對不起,請參閱編輯 –

+0

編輯看起來很優雅,但它似乎不適合我。 * f.baz *等會引發錯誤。 – bioneuralnet

1

有趣的問題。一點元編程應該照顧它。

module Attrs 
    def self.included(base) 
    base.extend ClassMethods 
    base.class_eval do 
     class << self 
     attr_accessor :attrs 
     end 
    end 
    end 

    module ClassMethods 
    # Define the attributes that each instance of the class should have 
    def has_attrs(*attrs) 
     self.attrs = attrs 
     attr_accessor *attrs 
    end 
    end 

    def initialize(*args) 
    raise ArgumentError, "You passed too many arguments!" if args.size > self.class.attrs.size 
    # Loop through each arg, assigning it to the appropriate attribute (based on the order) 
    args.each_with_index do |val, i| 
     attr = self.class.attrs[i] 
     instance_variable_set "@#{attr}", val 
    end 
    end 
end 

class Foo 
    include Attrs 
    has_attrs :bar, :buz 
end 

f = Foo.new('One', 'Two') 
puts f.bar 
puts f.buz 

當然,這個缺點是不靈活 - 你必須按照特定的順序傳遞你的構造函數參數。當然,這就是大多數編程語言。導軌人會說,你應該做的,而不是

f = Foo.new(:bar => 'One', :baz => 'Two') 

這將讓你在ATTRS通過以任意順序,以及剝離大部分元編程的路程。但是輸入的內容要多得多。

+0

感謝你的答案,但是每次寫'include Attrs'這行的必要性似乎讓我感到不愉快。而且,我不一定需要'attr_accessor's。 – sawa