Eine Möglichkeit, mehrere Rückgabewerte von einer Methode zurückzugeben: Setzen Sie die Methode in die Klasse, die den Rückgabewert darstellt. Ist es ein gutes Design?

15

Ich muss 2 Werte von einer Methode zurückgeben. Mein Ansatz ist wie folgt:

  1. Erstellen Sie eine innere Klasse mit 2 Feldern, die verwendet werden, um diese 2 Werte beizubehalten
  2. Fügen Sie die Methode in diese Klasse ein
  3. Instanziieren Sie die Klasse und rufen Sie die Methode auf.

Das einzige, was an der Methode geändert wird, ist, dass diese beiden Werte am Ende den Feldern der Instanz zugewiesen werden. Dann kann ich diese Werte ansprechen, indem ich mich auf die Felder dieses Objekts beziehe.

Ist es ein gutes Design und warum?

Dhblah
quelle
Eine andere Option (wahrscheinlich eine schlechte): siehe BitInteger[] java.math.BigInteger.divideAndRemainder(BitInteger val). Es gibt 2 Ganzzahlen als Rückgabewerte in einem Array zurück.
EarlNameless
Welchen Typ möchten Sie zurückgeben?
Tulains Córdova
Mögliches Duplikat - stackoverflow.com/questions/12668186/… .
m3th0dman

Antworten:

15

Ich würde dies folgendermaßen argumentieren:

  • Warum gibt Ihre Methode genau mehrere Werte zurück? Um welche Art von Zusammenhalt handelt es sich? Sollten diese Werte tatsächlich Felder in einer einzelnen Klasse sein, oder werden sie nur zufällig mit derselben Methode zurückgegeben, aber ansonsten ohne Bezug? In letzterem Fall können Sie die Methode in zwei Methoden aufteilen. Bearbeiten: Verwenden Sie Ihr Urteil hier; manchmal kann eine Art "zufälliger" Zusammenhalt die beste Option sein. Eine andere Option ist die Verwendung eines Paar- oder Tupelkonstrukts, obwohl diese in OOP normalerweise nicht in öffentlichen APIs angezeigt werden (einige bemerkenswerte Ausnahmen sind Standardauflistungen usw.).
  • Wenn die Werte es verdienen, eine Klasse zu bilden, würde ich wahrscheinlich davon abraten, eine innere Klasse zu verwenden. Innere Klassen werden normalerweise als interne Implementierungsdetails verwendet, die von außen verborgen sind. Gibt es einen Grund, warum dieses Ergebnis nicht für sich genommen eine "ausgewachsene" Klasse sein sollte?
  • Welche Operationen gelten für diese neue Klasse außer dem Halten von Daten? Im objektorientierten Design möchten Sie das zugehörige Verhalten in der Nähe der relevanten Daten haben (was auch Ihre Absichten zu sein scheinen). Sollte die Methode, auf die Sie sich beziehen, nicht eher von dieser Klasse leben?

Zusammenfassend würde ich sehen, ob ich dieses "Datenobjekt" in eine vollwertige Klasse mit Daten und Verhalten umwandeln kann. Als zusätzlichen Kommentar möchten Sie die Klasse möglicherweise unveränderlich machen, da ihr Status einmal festgelegt wird. Wenn Sie es unveränderlich machen, können Sie verhindern, dass es falsch eingestellt oder später geändert wird (z. B. wenn jemand eines der Felder auf null setzt und es weitergibt).

Bearbeiten: Wie Patkos Csaba richtig hervorhebt, wird hier das Prinzip der einmaligen Verantwortung ( Single Responsibility Principle, SRP ) angewendet. Die Klasse, die Sie erstellen möchten , sollte wirklich eine Verantwortung haben (als Grund für eine Änderung definiert ). Diese Gestaltungsrichtlinie soll Ihnen dabei helfen, herauszufinden, ob Ihre beiden Felder zu einer Klasse gehören oder nicht. Um beim Wikipedia-Beispiel zu bleiben, könnte Ihre Klasse als eine Art Bericht angesehen werden. In diesem Fall entspricht sie der SRP, ist jedoch ohne weitere Informationen schwer zu kommentieren.

Daniel B
quelle
Obwohl ich der allgemeinen Idee dieser Antwort zustimme, gibt es legitime Fälle, in denen zwei eng verwandte Daten zusammengerechnet werden, aber es hat keinen Sinn, sie an anderer Stelle im Programm miteinander zu verknüpfen. In einem solchen Fall kann es sauber genug sein, um so etwas zurückzugeben Pair<OneClass, AnotherClass>. Einige Leute würden nicht zustimmen . Sollte auf jeden Fall Pairein Implementierungsdetail sein und niemals in einer öffentlichen API-Methode erscheinen.
9000
@ 9000 Ich stimme zu; Ich meine eigentlich das Wort betrachtet buchstäblich das Verfahren die beste Lösung sein kann , nicht immer in diesem Fall, dh Aufspaltung genommen werden, es ist nur eine grobe Indikation in dieser Richtung. Ich werde einen Schnitt in diese Richtung machen.
Daniel B
1
Gute Antwort. Das einzige, was ich hinzufügen möchte, ist ein Verweis auf das Single Responsibility Principle (SRP) ( en.wikipedia.org/wiki/Single_responsibility_principle ). Wenn diese Option aktiviert ist, ist die diskutierte Methode möglicherweise nur eine einfache Verletzung von SRP, und eine Aufteilung ist eine einfache Lösung. Wenn eine Methode 2 oder mehr Werte zurückgeben möchte, gibt es nach meiner Erfahrung in 90% der Fälle 2 Methoden, oder es sollte eine andere Klasse extrahiert werden.
Patkos Csaba
@PatkosCsaba danke, habe eine Bearbeitung vorgenommen, um es aufzunehmen. Normalerweise bleibe ich bei der Erklärung von Dingen in Bezug auf Kopplung und Zusammenhalt, aber ich denke, die SOLID-Prinzipien werden als die Grundregeln angesehen, nach denen man heutzutage leben muss.
Daniel B
@DanielB: Ich sehe SOLID als übergeordnetes und wahrscheinlich besser verständliches Konzept. Kopplung und Kohäsion sind immer noch die Grundlinie, aber sie sind niedriger. SOLID nutzt Kopplung und Zusammenhalt in großem Maße, um seine Prinzipien zu erklären und sie allgemeiner darzustellen.
Patkos Csaba
10

Es gibt das Konzept eines Tupels, das in anderen Sprachen wie Python vorkommt.

Man könnte eine Instanz dieser generierten Klasse zurückgeben, die leicht wiederverwendbar ist:

public class TypedTuple<L, R> implements Serializable {
private static final long serialVersionUID = 1L;

  protected L left;
  protected R right;

  protected TypedTuple() {
    // Default constructor for serialization
  }

  public TypedTuple(L inLeft, R inRight) {
    left = inLeft;
    right = inRight;
  }

  public L getLeft() {
    return left;
  }

  public R getRight() {
    return right;
  }
}
Cuga
quelle
2
Manchmal ist es schön, eine generische statische Methode zu haben create(), um zu vermeiden, dass die Typparameter im Konstruktor angegeben werden müssen. Ich würde diese Klasse auch Paireher nennen als Tuple, da sie nur 2-Tupel-Werte darstellen kann.
August
5

Es scheint, dass diese Klasse einer anderen Klasse die Verantwortung abnimmt, und das lässt mich denken, dass dieser Entwurf nicht großartig ist.

Für eine Methode, die mehrere Werte zurückgibt, würde ich eher

  • gib einen generischen Container zurück (zB eine Liste oder Map), der die Rückgabewerte enthält

oder

  • Erstellen Sie eine Klasse für den Rückgabewert, die nur die erforderlichen Felder + Getter + einen Konstruktor mit allen Feldern enthält

Beispiel für die zweite Option:

public Class FooBarRetval {
   private String foo;
   private int bar;

   public FooBarRetval (String foo, int bar) {
      this.foo = foo;
      this.bar = bar;
   }

   public String getFoo() {
      return foo;
   }

   public int getBar() {
      return bar;
   }
}
user281377
quelle
Durch die Veröffentlichung der Felder besteht die Möglichkeit, die Werte separat zu ändern, obwohl die Werte eindeutig eine Beziehung haben (andernfalls müssten sie nicht mit derselben Methode zurückgegeben werden). Ich würde dieses Muster in dieser besonderen Situation sehr entmutigen. Der wichtigste Punkt ist jedoch, dass die Diskussion von öffentlichen und privaten Attributen nichts mit der Frage von OPs zu tun hat, und ich sehe keinen Grund für den letzten Satz in einer ansonsten guten Antwort.
scarfridge
Der erste sollte bevorzugt werden.
Juanin
scarfridge: Punkt genommen, letzter Satz entfernt.
User281377
2
Verwenden Sie einfach die öffentlichen Endfelder, ohne dass Sie Zeit mit Zugriffsmethoden verschwenden müssen.
August
0

Kurze Antwort: Sie können ein Array oder eine Liste mit den beiden Werten zurückgeben.

Ich persönlich würde gerne zwei verschiedene Methoden schreiben

int x = obj.getX();
int y = obj.getY();
Tulains Córdova
quelle