在Ruby中有没有办法重载初始化构造函数?

时间:2010-10-18 11:38:56

标签: ruby

在Java中,您可以重载构造函数:

public Person(String name) {
  this.name = name;
}
public Person(String firstName, String lastName) {
   this(firstName + " " + lastName);
}

Ruby中有没有办法实现同样的结果:两个构造函数采用不同的参数?

8 个答案:

答案 0 :(得分:70)

答案是肯定和否。

您可以使用各种机制在其他语言中获得与您相同的结果,包括: -

  • 参数的默认值
  • 变量参数列表(splat运算符)
  • 将参数定义为哈希

该语言的实际语法不允许您定义两次方法,即使参数不同。

考虑到上面的三个选项,可以使用您的示例实现,如下所示

# As written by @Justice
class Person
  def initialize(name, lastName = nil)
    name = name + " " + lastName unless lastName.nil?
    @name = name
  end
end


class Person
  def initialize(args)
    name = args["name"] + " " + args["lastName"] unless args["lastName"].nil?
    @name = name
  end
end

class Person
  def initialize(*args)
    #Process args (An array)
  end
end

你会经常在Ruby代码中遇到第二种机制,特别是在Rails中,因为它提供了两全其美的优点,并允许一些语法糖产生漂亮的代码,特别是不必将传递的哈希包含在大括号内。

wikibooks link提供更多阅读

答案 1 :(得分:25)

我倾向于

class Person
  def self.new_using_both_names(first_name, last_name)
    self.new([first_name, last_name].join(" "))
  end

  def self.new_using_single_name(single_name)
    self.new(single_name)
  end

  def initialize(name)
    @name = name
  end
end

但我不知道这是否是最佳方法。

答案 2 :(得分:4)

class Person
  def initialize(name, lastName = nil)
    name = name + " " + lastName unless lastName.nil?
    @name = name
  end
end

答案 3 :(得分:2)

class StatementItem
  attr_reader :category, :id, :time, :amount

  def initialize(item)
    case item
    when Order
      initialize_with_order(item)
    when Transaction
      initialize_with_transaction(item)
    end
  end

  def valid?
    !(@category && @id && @time && @amount).nil?
  end

  private
    def initialize_with_order(order)
      return nil if order.status != 'completed'
      @category = 'order'
      @id = order.id
      @time = order.updated_at
      @amount = order.price
    end

    def initialize_with_transaction(transaction)
      @category = transaction.category
      @id = transaction.id
      @time = transaction.updated_at
      @amount = transaction.amount
    end

end

答案 4 :(得分:1)

您可以使用konstructor gem在Ruby中声明多个构造函数并模仿重载:

class Person
  def initialize(name)
    @name = name
  end

  konstructor
  def from_two_names(first_name, last_name)
    @name = first_name + ' ' + last_name
  end
end

Person.new('John Doe')
Person.from_two_names('John', 'Doe')

答案 5 :(得分:1)

您可以将 double splat运算符 **||内的 logic或(双管道)initialize结合使用达到相同效果的方法。

class Person
  def initialize(**options)
    @name = options[:name] || options[:first_name] << ' ' << options[:last_name]
  end
end

james = Person.new(name: 'James')
#=> #<Person @name="James">

jill_masterson = Person.new(first_name: 'Jill', last_name: 'Masterson')
#=> #<Person @name="Jill Masterson">

但是,如果创建的新Person不包含first_name,则附加<<操作将失败,并显示NoMethodError: undefined method '<<' for nil:NilClass。这是一种重构的initialize方法来处理这种情况(如果排除了 个选项,则使用strip删除空格)。

class Person
  def initialize(**options)
    @name = options[:name] || [ options[:first_name] , options[:last_name] ].join(' ').strip
  end
end

goldfinger = Person.new(last_name: 'Goldfinger')
#=> #<Person @name="Goldfinger">

oddjob = Person.new(first_name: 'Oddjob')
#=> #<Person @name="Oddjob">

实际上,此方法处理不带参数或使用意外键的Person.new调用,以将@name设置为空字符串返回新实例:

nameless = Person.new
#=> <#Person @name="">

middle_malcom = Person.new(middle_name: 'Malcom')
#=> <#Person @name="">

答案 6 :(得分:0)

我通常会这样做:

class Person
  attr_reader :name
  def initialize(first: nil, last: nil)
    @name = [first, last].compact.join(' ')
  end
end

Person.new(first: 'ya').name
# => "ya"

Person.new(first: 'ya', last: 'ku').name
# => "ya ku"

答案 7 :(得分:0)

结帐functional-ruby的宝石,它受Elixir pattern matching功能的启发。

   class Foo
     include Functional::PatternMatching

     defn(:initialize) { @name = 'baz' }
     defn(:initialize, _) {|name| @name = name.to_s }
   end