2012-03-14 50 views
0

我发现我的自我重复的事情,当我在Ruby中创建类很多,我常常会落得类似下面的内容:在Ruby中定义类时避免重复?

class Foo 
    attr_reader :bar_0, 
       :bar_1, 
       . 
       . 
       . 
       :bar_n 
    def initialize(bar_0 = something, 
        bar_1 = something, 
         . 
         . 
         . 
        bar_n = something) 
    @bar_0 = bar_0 
    @bar_1 = bar_1 
      . 
      . 
      . 
    @bar_n = bar_n 
    end 
end 

是否红宝石采用更有效地实施这样的一条捷径?

+0

你究竟想要做什么?具有50个参数的构造函数在任何语言中都很难看。 – 2012-03-14 19:48:27

+0

这不是我想包括已经包含的东西,我只是希望能够在类上调用new时定义实例变量('@bar = bar'),为new设置默认值(bar =(无)),并使其可访问('attr_reader'),而不必重复我自己三次 – rudolph9 2012-03-14 20:01:36

回答

1

从问题的表达方式来看,您应该重新考虑您的课程设计。但是,Ruby提供了一种有趣的方式来快速创建与attr_accessor(不是读者)的类。这里有一个简单的例子:

>> class Person < Struct.new(:name, :age) ; end 
=> nil 
>> p = Person.new 
=> #<struct Person name=nil, age=nil> 
>> p.age = 23 
=> 23 
>> p.class 
=> Person 
>> p.methods.grep(/age/) 
=> [:age, :age=] 

当然,这是一个普通的类,你可以添加你想要的所有方法(和使用getter和setter方法,而不是实例变量,例如var的getter和self.var = foo的二传手) 。

如果你真的不希望作家,使他们私人或undef他们。

>> attrs = [:name, :age] 
=> [:name, :age] 
>> class Person < Struct.new *attrs ; end 
=> nil 
>> Person.instance_eval { private *attrs.map{|attr| "#{attr}=" }} 
=> Person 
>> p = Person.new 
=> #<struct Person name=nil, age=nil> 
>> p.methods.grep(/age/) 
=> [:age] 

上述所有不随当然initialize的万吨任务的帮助,但后来人们不禁要问,如果你真的想许多构造函数的参数,或者,也许你只是有一个哈希参数和合并到这默认散列。

+0

我一直想知道天气只是把所有的东西放在这么多的构造函数变量的散列,但我想有任何一种方法的初始化可用。本质上,我正在编写一个Gem来与REST API进行接口,并且我正在努力尽可能有利于它所包含的任何项目。具体来说,我希望从REST调用中提取的每个变量都可以通过'p = Foo.new(argument_0,...,argument_n)'和/或'p = Foo.new(hash)'获得并且有任何参数可用通过'p.bar_i'。有点java的心态,但有用的不少。 – rudolph9 2012-03-15 06:04:33

0

Ruby是动态的,在内省方面提供了很多内容,因此您可以使用元编程(或编写基本上编写代码的代码)。在您的例子有几件事情可以做,以清理冗长:

class Foo 
    # Rather than writing bar_1, bar_2, bar_3, ... 
    attr_accessor ((0..9).to_a + ('a'..'n').to_a).map { |x| :"foo_#{x}" } 

    # Using mass assignment... 
    def initialize(attributes = {}) 
    attributes.each do |attribute, value| 
     respond_to?(:"#{attribute}=") && send(:"#{attribute}=", value) 
    end 
    end 
end 

由于质量分配是一个普遍的和可重复使用的行为,它是有道理的其解压缩到一个单独的模块,使之成为混入:

module MassAssignment 
    def initialize(attributes = {}) 
    mass_assign(attributes) 
    end 

    def mass_assign(attributes) 
    attributes.each do |attribute, value| 
     respond_to?(:"#{attribute}=") && send(:"#{attribute}=", value) 
    end 
    end 
end 

class Foo 
    include MassAssignment 
end 
+0

'attr_reader((0..9).to_a +('a'..'n').to_a).map { | X | :“foo _#{x}”}'我认为这是WTF类型代码的很酷的例子。 Ruby可以做到这一点,但我认为它不应该被其他任何人看到的实时代码使用。 – iafonov 2012-03-14 20:02:27

+1

不一定是bar_i实例变量的一致命名约定,我需要能够设置默认值。我希望能够在一个定义中完成我现在所做的三个单独定义。 – rudolph9 2012-03-14 20:03:37

+1

在1.9中,你可以像这样创建数组:'[* 0..9,*?a ..?n]'。 :-) – 2012-03-14 20:04:30