Wann ist eine Methodenüberladung angebracht?

10

Angenommen, ich arbeite an einem vorhandenen, relativ großen System. Ich habe ein Objekt myObjectder Klasse MyClass(zum Beispiel nehme ich an, ich arbeite in Java). myObjectist eine Komposition Collection, die beispielsweise a Listund andere Objekte enthält, die (glaube ich) irrelevant sind. Es enthält Delegate-Methoden, die nur dazu dienen, die Methoden aufzurufen, aus denen Listes besteht, um sicherzustellen, dass Listes nicht verfügbar gemacht wird (Entschuldigung, wenn meine Terminologie falsch ist).

Nehmen wir an, dies Listist eine, List<String>aber aus irgendeinem Grund ist die Hauptzugriffsmethode eine Maskenmethode für die Klasse SomeOtherClass. Wenn ich ein neues Wertepaar in mein einfügen wollte, Listhätte ich ein Objekt SomeOtherClassnamens aufgerufen someObject. Ich würde anrufen myObject.insert(someObject)und innerhalb der insertMethode würde es etwas Magie geben, die ein abrufen würde String, um es in das zu setzen List<String>.

Angenommen, ich habe nur einen StringWert und kein SomeOtherClassObjekt zum Einfügen. Angenommen, ich kann die insertMethode nicht ändern , da sie alles in diesem System beschädigen würde. Dann sollte ich die insertMethode überladen ? Oder sollte ich SomeOtherClassjedes Mal ein neues Objekt erstellen, wenn ich anrufen möchte insert?

Ich denke, wenn ich es überladen würde, würde es ungefähr so ​​aussehen ...

public void insert(String s) {
    ...
}

public void insert(SomeOtherObject obj) {
    this.insert(obj.magicStringMethod());
}

(Dieses Beispiel ist ein erfundenes Rätsel, das auf einer ähnlichen (etwas komplexeren) Situation in Bezug auf Überlastung basiert, auf die ich gestern gestoßen bin. Ich werde es erweitern, wenn etwas unklar ist.)

Wäre dies ein geeigneter Ort, um eine Methode zu überladen? Wenn nicht, wann sollte ich eine Methode überladen?

blaman
quelle
Angenommen, Java, haben Sie sich mit der Verwendung von Generika befasst, um dieses Problem zu lösen? Ich denke, was ich wirklich frage, ist, was der String wirklich darstellt. Wenn es sich tatsächlich um eine Darstellung eines tatsächlichen Domänenobjekts handelt, gibt es einen anderen möglichen Weg, um dieses Problem zu lösen
Martijn Verburg
@MartijnVerburg habe ich noch nicht. Da ich nur ein Praktikant bin, bin ich mit Dingen wie Designmustern noch ein wenig unbekannt und habe noch keine guten Gewohnheiten in der Designpraxis. Bei der Beantwortung Ihrer zweiten Frage handelt es sich um eine Darstellung eines tatsächlichen Domänenobjekts. magicStringMethod()In meinem Beispiel würde in meinem Fall eine StringDarstellung erhalten, bei SomeOtherObjectder es sich um ein Domänenobjekt handelt.
Blahman
Ich schlage vor, Sie haben sich von Überlastung ferngehalten, wenn Sie können. Es verursacht manchmal Verwirrung. Es ist am besten, zur Entwurfszeit sicher zu sein, welche Methode aufgerufen wird. Siehe dies: programmers.stackexchange.com/questions/132369/…
NoChance

Antworten:

7

Sie überlasten, wenn Sie verschiedene Typen unterstützen möchten:

public overload void MyMethod(int value)
{ 
}

public overload void MyMethod(bool value)
{
}

public overload void MyMethod(string value)
{
}

oder um eine progressive Schnittstelle mit verschiedenen Parameterlisten zu unterstützen:

public overload void MyOtherMethod()
{
    this.MyOtherMethod(DefaultValue);
}

public overload void MyOtherMethod(int value)
{
    this.MyOtherMethod(value, DefaultOtherValue);
}

public overload void MyOtherMethod(int value, bool otherValue)
{
    ...
}

Sie werden vielleicht sogar ein bisschen verrückt und unterstützen sowohl verschiedene Typen als auch eine progressive Benutzeroberfläche, aber Sie sollten bedenken, dass Sie vermeiden müssen, dass sich das Verhalten zwischen überladenen Methoden ändert. Jede überladene Methode sollte funktional mit den anderen in einer überladenen Gruppe identisch sein, da sonst unklar ist, wie, wann oder warum das Verhalten variiert wird. Wenn Sie möchten, dass zwei Funktionen etwas völlig anderes tun, sollten Sie sie entsprechend benennen.

S.Robins
quelle
7

Ich würde sagen, dass eine Überladung angemessen ist, wenn beide Methoden semantisch äquivalent sind. Um aus den Beispielen von dukeofgaming zu stehlen:

Diese sind entsprechend überlastet:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a+b;
}

Diese sind nicht:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a-b;
}

Das ist ein extremes Beispiel (wenn Sie es nicht verstanden haben, subtrahiert die letzte Methode tatsächlich anstatt zu addieren), aber die Idee ist, dass sich mehrere Methoden in einer Klasse mit demselben Namen konsistent verhalten sollten, wenn Sie sie haben.

In Ihrem Beispiel scheinen die angegebenen Informationen gleichwertig zu sein (da einer den anderen anruft), und ich halte es für vernünftig, sie zu überladen. Es kann nicht schaden, jemanden zu fragen, der älter in Ihrem Team ist, wenn Sie sich nicht sicher sind, ob die Methode hinzugefügt werden soll, aber wenn Sie sie hinzufügen würden, würde ich denselben Namen verwenden (dh ich würde sie überladen).

Gyan alias Gary Buyn
quelle
1
Danke für die Antwort. Ich habe jemanden gefragt, der älter ist, aber da sich der Code in einem etwas interessanten Zustand befindet, war seine Lösung eine, bei der ich mir nicht sicher war, die ich aber dennoch implementiert habe (es war nicht die Überladung).
Blahman
5

Im Wesentlichen bestimmen die Parameter der Methode, wie sich die Methode verhält.

Ein kurzes Beispiel wäre:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    //...
}

Wenn Sie die Methodensumme (a, b) ein paar Ganzzahlen übergeben, weiß sie, dass sie die erste Implementierung aufrufen sollte, da der Methodenaufruf mit der Methodensignatur übereinstimmt (dh die doppelte Summe (double, double) funktioniert nicht, wenn Sie die angeben Summenmethode zwei ganze Zahlen).

Die Signatur des Aufrufs muss mit den verfügbaren Implementierungen übereinstimmen. Der Versuch, sum (string, string) aufzurufen, funktioniert daher nur, wenn Sie Folgendes haben:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    public string sum(String a, String b){
        return "" + (Double.parseDouble(a) + Double.parseDouble(b));
    }
    //...
}

tl; dr: Damit die Klasse die richtige Methode gemäß den Parametern behandelt, geben Sie eine Methode mit demselben Namen an.

Beim Erben wird es als Überschreiben bezeichnet

Ein gutes Beispiel für dieses andere Szenario ist, wenn Sie das Standardverhalten einer Klasse durch Erben ändern möchten, und insbesondere, wenn Sie etwas Ähnliches wie das Muster der Vorlagenmethode haben, bei dem jeder Schritt eines Algorithmus in einer Methode implementiert ist.

Stellen Sie sich vor, Sie haben class Roboteine Methode namens fireAtTarget(Target target)..., die fireWeaponA(target); fireWeaponB(target); fireWeaponC(target);nacheinander aufruft . Sie möchten auch eine Sammlung namens robot_army haben, zu der Sie nur Objekte der Klasse Robot hinzufügen können. Roboter feuert standardmäßig Maschinengewehre in seinen fireWeaponX()Methoden ab.

Dann möchten Sie haben class LazorRobotund class MissileRobot, hast du Roboter implementieren alle immer wieder ?, nein, nur LazorRobot und MissileRobot vererben Roboter haben, und überlasten jedes fireWeaponX()Verfahren zur Verwendung lazorz oder Raketen , und Sie werden das gleiche Verhalten mit verschiedenen Waffen haben, ohne neu zu implementieren, die der Rest der Methoden.

tl; dr: Damit das Verhalten der Methode von der Klasse abhängt, ohne die Schnittstelle zu beschädigen (robot_army akzeptiert nur Robot, akzeptiert jedoch alle Klassen, die Robot erben).

dukeofgaming
quelle
Ihr erstes Beispiel ist ein Override . Wenn Sie die Schnittstelle einer Methode beibehalten und dennoch das Verhalten eines Nachkommen ändern. Die Implikation ist, dass Sie nicht beabsichtigen, eine alternative Methode anzubieten. Ihr zweites Beispiel ist jedoch korrekt überladen. Wenn Sie hervorheben möchten, wann überschrieben werden muss und wann überladen werden soll, möchten Sie dies möglicherweise in Ihrer Antwort klarer formulieren, da sonst when inheritingwahrscheinlich nicht der gesamte Abschnitt benötigt wird. :)
S.Robins
Derp, Koffein hat mich diesmal erwischt = P, hat den zweiten Teil als ersten und den ersten als zweiten Teil aus allgemeinem Interesse gelassen. Danke für die Korrektur.
Dukeofgaming
Wenn ich Ihre Antwort richtig verstanden habe , sollte ich nur eine Überlastung , wenn sie bei den Parametern sucht , wird mir erlauben , die Unterschiede im Verhalten zwischen, sagen wir ableiten, sum(String, String)und sum(int, int)?
Blahman
@blahman sollten Sie überladen, wenn Sie verschiedene Typen, eine Mischung von Typen oder sogar zusätzliche Parameter hinzufügen möchten. Durch Überladen können Methoden mit Standardparametern erstellt werden, bei denen die Standardsyntax param = value nicht unterstützt wird. Sehen Sie meine Antwort, um zu sehen, was ich meine.
S.Robins
1
@blahman Übrigens und zu einem anderen Thema, wenn Sie zu viele Parameter haben und einige davon optional sind, ist es manchmal besser, nur ein einzelnes Objekt oder eine einzelne Datenstruktur mit allen Standardwerten zu senden, damit Sie die Attribute eines solchen Objekts oder solcher Daten ändern Struktur. Auf diese Weise müssen Sie nicht 20 Versionen derselben Methode erstellen, um jedes Mal einen einzelnen Parameter hinzuzufügen.
Dukeofgaming