Gibt es in Ruby eine Möglichkeit, den Initialisierungskonstruktor zu überladen?

74

In Java können Sie Konstruktoren überladen:

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

Gibt es in Ruby eine Möglichkeit, dasselbe Ergebnis zu erzielen: zwei Konstruktoren, die unterschiedliche Argumente verwenden?

agentbanks217
quelle

Antworten:

78

Die Antwort ist sowohl Ja als auch Nein.

Sie können das gleiche Ergebnis wie in anderen Sprachen erzielen, indem Sie verschiedene Mechanismen verwenden, darunter:

  • Standardwerte für Argumente
  • Variablenargumentlisten (Der Splat-Operator)
  • Definieren Sie Ihr Argument als Hash

Die tatsächliche Syntax der Sprache erlaubt es Ihnen nicht, eine Methode zweimal zu definieren, selbst wenn die Argumente unterschiedlich sind.

In Anbetracht der drei oben genannten Optionen können diese anhand Ihres Beispiels wie folgt implementiert werden

# 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"]
    name = name + " " + args["lastName"] unless args["lastName"].nil?
    @name = name
  end
end

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

Sie werden häufig auf den zweiten Mechanismus in Ruby-Code stoßen, insbesondere in Rails, da er das Beste aus beiden Welten bietet und es syntaktischem Zucker ermöglicht, hübschen Code zu erzeugen, insbesondere, wenn der übergebene Hash nicht in geschweifte Klammern eingeschlossen werden muss.

Dieser Wikibooks-Link bietet weitere Informationen

Steve Weet
quelle
Vielen Dank für den Hinweis auf die Person.new(:first_name => "...", :last_name => "...")Methode. Aus irgendeinem Grund kam es mir nicht in den Sinn, das zu benutzen, aber das beantwortet meine Frage.
Agentbanks217
5
Ruby 2.0 unterstützt sofort benannte Parameter: robots.thoughtbot.com/ruby-2-keyword-arguments
Felipe
27

Ich neige dazu zu tun

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

Aber ich weiß nicht, ob dies der beste Ansatz ist.

Andrew Grimm
quelle
1
+1: Nicht das Beste, aber auf jeden Fall klug. Folgt Rubys Namenssystem.
André Leria
+1 für die clevere Idee, obwohl dies per Definition nicht überladen ist.
Karthick S
4
class Person
  def initialize(name, lastName = nil)
    name = name + " " + lastName unless lastName.nil?
    @name = name
  end
end
Yfeldblum
quelle
3
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
Hexinpeter
quelle
1

Sie können den Double-Splat-Operator ** in Verbindung mit logischen oder (Double-Pipes) ||innerhalb der initializeMethode verwenden, um den gleichen Effekt zu erzielen.

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">

Wenn jedoch eine neue Personohne a erstellt wird first_name, schlägt der Append- <<Vorgang mit fehl NoMethodError: undefined method '<<' for nil:NilClass. Hier ist eine überarbeitete initializeMethode, um diesen Fall zu behandeln (Verwenden stripzum Entfernen von Leerzeichen, wenn eine der beiden Optionen ausgeschlossen ist).

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">

Tatsächlich behandelt dieser Ansatz den Aufruf Person.newohne Argumente oder mit einem unerwarteten Schlüssel, um die neue Instanz mit @nameset auf eine leere Zeichenfolge zurückzugeben:

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

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

Kasse funktional-rubinroter Edelstein, der von Elixir- Muster-Matching- Funktionen inspiriert ist.

   class Person
     include Functional::PatternMatching

     defn(:initialize, String) { |name| 
       @name = name 
     }

     defn(:initialize, String, String) {|first_name, last_name| 
      @name = first_name + ' ' + last_name
     }
   end
Oshan Wisumperuma
quelle
1
Dies ist eine innovative Möglichkeit, Praktiken aus einer anderen Sprache auszuleihen. Ich mag Ihr Codebeispiel , um zu sehen mehr ähnelt eng das ursprüngliche Plakat Sprache mit Person, first_nameund last_name.
Chemturion
0

Sie können konstructor gem verwenden, um mehrere Konstruktoren in Ruby zu deklarieren und eine Überladung zu imitieren:

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')
Snovity
quelle