2011-10-21 25 views
6

Powiel możliwe:
Idiomatic object creation in rubyWłączanie argumenty konstruktora do zmiennych instancji

Istnieje wiele okazjach, kiedy mam metodę initialize, który wygląda tak:

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

Czy można to zrobić za pomocą prostej komendy, takiej jak:

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

, w którym symbole oznaczają nazwy zmiennych przykład (z duchem/smaku attr_accessor, attr_reader, attr_writer)?


Zastanawiałem się, czy nie jest wbudowany w sposób lub bardziej elegancki sposób zrobić coś takiego:

class Class 
    def attr_constructor *vars 
     define_method("initialize") do |*vals| 
      vars.zip(vals){|var, val| instance_variable_set("@#{var}", val)} 
     end 
    end 
end 

tak, że można go używać tak:

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

Czy chcesz zainicjować atrybuty podczas tworzenia? –

+1

Myślę, że to pytanie zostało zadane wcześniej, ale nie znalazłem żadnych dokładnych duplikatów. –

+0

@AndrewGrimm Dzięki za odniesienie do duplikatu pytania. Zobaczę tam odpowiedzi. Będę głosować, aby sam to zamknąć. – sawa

Odpowiedz

1

Czy to działa dla Ciebie?

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

To nie jest dokładnie to, czego chcę. Chcę, aby metoda klasy była używana raz w ciele klasy, która kontroluje zachowanie 'initialize'. – sawa

1

użyję OpenStruct:

require 'ostruct' 

class Foo < OpenStruct 
end 

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

EDIT: Ah OK, przepraszam cię niezrozumiany. Co powiesz na:

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

W swoim kodzie, musisz podać nazwę zmiennej instancji w konstrukcji, co jest gorsze niż zdefiniowanie tego w metodzie 'initialize'. – sawa

+0

Ah przepraszam, zobacz edytuj –

+0

Edycja wygląda bardzo elegancko, ale nie wydaje się to dla mnie działać. * f.baz * etc zgłasza błędy. – bioneuralnet

1

Interesujące pytanie. Powinno to zająć trochę meta-programowania.

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 

Oczywiście wadą tego jest brak elastyczności - trzeba przekazać argumenty konstruktora w określonej kolejności. Oczywiście tak wygląda większość języków programowania. Poręcze ludzie mogą twierdzić, należy zamiast robić

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

który pozwoliłby, aby przejść w attrs w dowolnej kolejności, a także znieść większość meta-programowania. Ale to dużo więcej do pisania.

+0

Dzięki za odpowiedź, ale konieczność napisania wiersza 'include Attrs' za każdym razem wydaje mi się nieprzyjemna.I niekoniecznie potrzebuję 'attr_accessor's. – sawa

Powiązane problemy