Antworten:
Das Überladen von Methoden kann erreicht werden, indem zwei Methoden mit demselben Namen und unterschiedlichen Signaturen deklariert werden. Diese unterschiedlichen Signaturen können entweder
method(int a, int b) vs method(String a, String b)
method(a) vs method(a, b)
Wir können keine Methodenüberladung auf die erste Weise erreichen, da es in Ruby ( dynamisch typisierte Sprache ) keine Datentypdeklaration gibt . Die einzige Möglichkeit, die obige Methode zu definieren, istdef(a,b)
Mit der zweiten Option könnte es so aussehen, als könnten wir eine Methodenüberladung erreichen, aber wir können nicht. Angenommen, ich habe zwei Methoden mit unterschiedlicher Anzahl von Argumenten.
def method(a); end;
def method(a, b = true); end; # second argument has a default value
method(10)
# Now the method call can match the first one as well as the second one,
# so here is the problem.
Ruby muss also eine Methode in der Methodensuchkette mit einem eindeutigen Namen beibehalten.
"Überladen" ist ein Begriff, der in Ruby einfach keinen Sinn ergibt. Es ist im Grunde ein Synonym für „statischen Argument-basierten Versand“, aber Rubin nicht hat statische Dispatch überhaupt . Der Grund, warum Ruby den statischen Versand basierend auf den Argumenten nicht unterstützt, liegt darin, dass der statische Versand nicht unterstützt wird. Es unterstützt keinen statischen Versand von jeder Art , ob Argument-basierte oder auf andere Weise.
Wenn Sie nicht speziell nach Überladung fragen, sondern nach dynamischem argumentbasiertem Versand, lautet die Antwort: Weil Matz sie nicht implementiert hat. Weil sich sonst niemand die Mühe gemacht hat, es vorzuschlagen. Weil sich sonst niemand die Mühe gemacht hat, es umzusetzen.
Im Allgemeinen ist es sehr schwierig, einen dynamischen argumentbasierten Versand in einer Sprache mit optionalen Argumenten und Argumentlisten variabler Länge richtig zu machen und noch schwieriger , ihn verständlich zu halten. Selbst in Sprachen mit statischem argumentbasiertem Versand und ohne optionale Argumente (wie zum Beispiel Java) ist es manchmal fast unmöglich, für einen bloßen Sterblichen zu sagen, welche Überladung ausgewählt werden soll.
In C # können Sie tatsächlich jedes 3-SAT-Problem in eine Überlastungsauflösung codieren , was bedeutet, dass die Überlastungsauflösung in C # NP-hart ist.
Versuchen Sie dies jetzt mit dem dynamischen Versand, bei dem Sie die zusätzliche Zeitdimension haben, die Sie im Kopf behalten müssen.
Es gibt Sprachen, die dynamisch basierend auf allen Argumenten einer Prozedur versenden, im Gegensatz zu objektorientierten Sprachen, die nur mit dem "versteckten" nullten self
Argument versenden . Common Lisp sendet beispielsweise die dynamischen Typen und sogar die dynamischen Werte aller Argumente aus. Clojure sendet eine willkürliche Funktion aller Argumente (was übrigens extrem cool und extrem mächtig ist).
Ich kenne jedoch keine OO-Sprache mit dynamischem argumentbasiertem Versand. Martin Odersky sagte, dass er erwägen könnte , Scala einen argumentbasierten Versand hinzuzufügen, aber nur, wenn er gleichzeitig die Überladung entfernen und abwärtskompatibel sein kann, sowohl mit vorhandenem Scala-Code, der Überladung verwendet, als auch kompatibel mit Java (er erwähnte insbesondere Swing und AWT) die einige extrem komplexe Streiche spielen, die so ziemlich jeden bösen dunklen Eckfall von Javas ziemlich komplexen Überladungsregeln ausüben). Ich hatte selbst einige Ideen, wie ich Ruby einen argumentbasierten Versand hinzufügen kann, aber ich konnte nie herausfinden, wie ich es abwärtskompatibel machen kann.
def method(a, b = true)
nicht, daher ist das Überladen von Methoden nicht möglich." Es ist nicht; es ist nur schwierig. Ich fand diese Antwort jedoch sehr informativ.
Ich nehme an, Sie suchen nach der Möglichkeit, dies zu tun:
def my_method(arg1)
..
end
def my_method(arg1, arg2)
..
end
Ruby unterstützt dies auf andere Weise:
def my_method(*args)
if args.length == 1
#method 1
else
#method 2
end
end
Ein gängiges Muster ist auch die Übergabe von Optionen als Hash:
def my_method(options)
if options[:arg1] and options[:arg2]
#method 2
elsif options[:arg1]
#method 1
end
end
my_method arg1: 'hello', arg2: 'world'
hoffentlich hilft das
Das Überladen von Methoden ist in einer Sprache mit statischer Typisierung sinnvoll, in der Sie zwischen verschiedenen Arten von Argumenten unterscheiden können
f(1)
f('foo')
f(true)
sowie zwischen unterschiedlicher Anzahl von Argumenten
f(1)
f(1, 'foo')
f(1, 'foo', true)
Die erste Unterscheidung existiert nicht in Rubin. Ruby verwendet dynamisches Tippen oder "Enten-Tippen". Die zweite Unterscheidung kann durch Standardargumente oder durch Arbeiten mit Argumenten erfolgen:
def f(n, s = 'foo', flux_compensator = true)
...
end
def f(*args)
case args.size
when
...
when 2
...
when 3
...
end
end
Dies beantwortet nicht die Frage, warum Ruby keine Methodenüberladung hat, aber Bibliotheken von Drittanbietern können diese bereitstellen.
Die contract.ruby- Bibliothek ermöglicht das Überladen. Beispiel aus dem Tutorial angepasst:
class Factorial
include Contracts
Contract 1 => 1
def fact(x)
x
end
Contract Num => Num
def fact(x)
x * fact(x - 1)
end
end
# try it out
Factorial.new.fact(5) # => 120
Beachten Sie, dass dies tatsächlich leistungsfähiger ist als die Überladung von Java, da Sie Werte angeben können, die übereinstimmen (z. B. 1
), nicht nur Typen.
Sie werden jedoch eine verminderte Leistung feststellen, wenn Sie dies verwenden. Sie müssen Benchmarks durchführen, um zu entscheiden, wie viel Sie tolerieren können.
Ich mache oft die folgende Struktur:
def method(param)
case param
when String
method_for_String(param)
when Type1
method_for_Type1(param)
...
else
#default implementation
end
end
Auf diese Weise kann der Benutzer des Objekts die saubere und klare Methode method_name: method verwenden. Wenn er jedoch die Ausführung optimieren möchte, kann er direkt die richtige Methode aufrufen.
Außerdem werden Ihre Tests klarer und besser.
Es gibt bereits gute Antworten auf die Warum-Seite der Frage. Wenn Sie jedoch nach anderen Lösungen suchen, schauen Sie sich das funktionale Rubinjuwel an, das von den Elixir- Mustervergleichsfunktionen inspiriert ist.
class Foo
include Functional::PatternMatching
## Constructor Over loading
defn(:initialize) { @name = 'baz' }
defn(:initialize, _) {|name| @name = name.to_s }
## Method Overloading
defn(:greet, :male) {
puts "Hello, sir!"
}
defn(:greet, :female) {
puts "Hello, ma'am!"
}
end
foo = Foo.new or Foo.new('Bar')
foo.greet(:male) => "Hello, sir!"
foo.greet(:female) => "Hello, ma'am!"