it-swarm.com.de

Welcher Stil ist besser (Instanzvariable vs. Rückgabewert) in Java

Ich habe oft Schwierigkeiten zu entscheiden, welche dieser beiden Möglichkeiten ich verwenden soll, wenn ich für einige Methoden in meinen Klassen gemeinsame Daten verwenden muss. Was wäre eine bessere Wahl?

Mit dieser Option kann ich eine Instanzvariable erstellen, um zu vermeiden, dass zusätzliche Variablen deklariert werden müssen, und um zu vermeiden, dass Methodenparameter definiert werden. Es ist jedoch möglicherweise nicht so klar, wo diese Variablen instanziiert/geändert werden:

public class MyClass {
    private int var1;

    MyClass(){
        doSomething();
        doSomethingElse();
        doMoreStuff();
    }

    private void doSomething(){
        var1 = 2;
    }

    private void doSomethingElse(){
        int var2 = var1 + 1;
    }

    private void doMoreStuff(){
        int var3 = var1 - 1;
    }
}

Oder einfach lokale Variablen instanziieren und als Argumente übergeben?

public class MyClass {  
    MyClass(){
        int var1 = doSomething();
        doSomethingElse(var1);
        doMoreStuff(var1);
    }

    private int doSomething(){
        int var = 2;
        return var;
    }

    private void doSomethingElse(int var){
        int var2 = var + 1;
    }

    private void doMoreStuff(int var){
        int var3 = var - 1;
    }
}

Wenn die Antwort lautet, dass beide korrekt sind, welche wird häufiger gesehen/verwendet? Auch wenn Sie zusätzliche Vor-/Nachteile für jede Option angeben können, wäre dies sehr wertvoll.

34
carlossierra

Ich bin überrascht, dass dies noch nicht erwähnt wurde ...

Es hängt davon ab, ob var1 Tatsächlich Teil des Status Ihres Objekts ist .

Sie gehen davon aus, dass beide Ansätze korrekt sind und dass es nur um Stil geht. Sie liegen falsch.

Hier geht es ausschließlich darum, wie man richtig modelliert.

In ähnlicher Weise existieren private Instanzmethoden, um den Status Ihres Objekts zu mutieren. Wenn Ihre Methode dies nicht tut, sollte es private static Sein.

108
MetaFight

Ich weiß nicht, was häufiger vorkommt, aber ich würde immer Letzteres tun. Es kommuniziert den Datenfluss und die Lebensdauer klarer und bläht nicht jede Instanz Ihrer Klasse mit einem Feld auf, dessen einzige relevante Lebensdauer die Initialisierung ist. Ich würde sagen, Ersteres ist nur verwirrend und erschwert die Codeüberprüfung erheblich, da ich die Möglichkeit in Betracht ziehen muss, dass eine Methode var1.

17

Sie sollten den mfang Ihrer Variablen so weit wie möglich (und vernünftig) reduzieren. Nicht nur in Methoden, sondern allgemein.

Für Ihre Frage bedeutet dies, dass es davon abhängt, ob die Variable Teil des Objektstatus ist. Wenn ja, ist es in Ordnung, es in diesem Bereich zu verwenden, d. H. Im gesamten Objekt. In diesem Fall gehen Sie mit der ersten Option. Wenn nein, wählen Sie die zweite Option, da sie die Sichtbarkeit von Variablen und damit die Gesamtkomplexität verringert.

7
Andy

Welcher Stil ist in Java besser (Instanzvariable vs. Rückgabewert)?

Es gibt einen anderen Stil - verwenden Sie einen Kontext/Status.

public static class MyClass {
    // Hold my state from one call to the next.
    public static final class State {
        int var1;
    }

    MyClass() {
        State state = new State();
        doSomething(state);
        doSomethingElse(state);
        doMoreStuff(state);
    }

    private void doSomething(State state) {
        state.var1 = 2;
    }

    private void doSomethingElse(State state) {
        int var2 = state.var1 + 1;
    }

    private void doMoreStuff(State state) {
        int var3 = state.var1 - 1;
    }
}

Dieser Ansatz bietet eine Reihe von Vorteilen. Die Statusobjekte können sich beispielsweise unabhängig vom Objekt ändern, was viel Spielraum für die Zukunft bietet.

Dies ist ein Muster, das auch in einem verteilten/Server-System gut funktioniert, in dem einige Details über Anrufe hinweg beibehalten werden müssen. Sie können Benutzerdetails, Datenbankverbindungen usw. im Objekt state speichern.

5
OldCurmudgeon

Es geht um Nebenwirkungen.

Die Frage, ob var1 Teil des Staates ist, geht am eigentlichen Punkt dieser Frage vorbei. Sicher, wenn var1 Anhalten muss, muss es eine Instanz sein. Es kann jeder Ansatz gewählt werden, um zu funktionieren, unabhängig davon, ob Persistenz erforderlich ist oder nicht.

Der Nebenwirkungsansatz

Einige Instanzvariablen werden nur zur Kommunikation zwischen privaten Methoden von Aufruf zu Aufruf verwendet. Diese Art von Instanzvariable kann aus der Existenz heraus überarbeitet werden, muss es aber nicht sein. Manchmal sind die Dinge mit ihnen klarer. Dies ist jedoch nicht ohne Risiko.

Sie lassen eine Variable aus ihrem Bereich heraus, da sie in zwei verschiedenen privaten Bereichen verwendet wird. Nicht, weil es in dem Bereich benötigt wird, in dem Sie es platzieren. Dies kann verwirrend sein. Die "Globalen sind böse!" Grad der Verwirrung. Dies kann funktionieren, lässt sich aber nicht gut skalieren. Es funktioniert nur im kleinen. Keine großen Gegenstände. Keine langen Vererbungsketten. Verursachen Sie keinen yo yo Effekt.

Der funktionale Ansatz

Selbst wenn var1 Fortbestehen muss, bedeutet nichts, dass Sie verwenden müssen, wenn für jeden vorübergehenden Wert, den er annehmen kann, bevor er den Status erreicht, den Sie zwischen öffentlichen Aufrufen beibehalten möchten. Das heißt, Sie können eine var1 - Instanz weiterhin nur mit funktionaleren Methoden festlegen.

Ob Teil des Staates oder nicht, Sie können immer noch einen der beiden Ansätze verwenden.

In diesen Beispielen ist 'var1' so gekapselt, dass nichts außer Ihrem Debugger weiß, dass es existiert. Ich vermute, Sie haben das absichtlich getan, weil Sie uns nicht voreingenommen machen wollen. Zum Glück ist es mir egal, welche.

Das Risiko von Nebenwirkungen

Das heißt, ich weiß, woher Ihre Frage kommt. Ich habe unter miserabler yo yo 'Vererbung gearbeitet, die eine Instanzvariable auf mehreren Ebenen in mehreren Methoden mutiert und eichhörnchenhaft versucht hat, ihr zu folgen. Das ist das Risiko.

Dies ist der Schmerz, der mich zu einem funktionaleren Ansatz treibt. Eine Methode kann ihre Abhängigkeiten dokumentieren und in ihrer Signatur ausgeben. Dies ist ein kraftvoller, klarer Ansatz. Außerdem können Sie ändern, was Sie an der privaten Methode übergeben, um sie innerhalb der Klasse wiederverwendbarer zu machen.

Die Vorteile von Nebenwirkungen

Es ist auch einschränkend. Reine Funktionen haben keine Nebenwirkungen. Das kann eine gute Sache sein, ist aber nicht objektorientiert. Ein großer Teil der Objektorientierung ist die Fähigkeit, auf einen Kontext außerhalb der Methode zu verweisen. Das zu tun, ohne hier und da Globale zu verlieren, ist die Stärke von OOP. Ich bekomme die Flexibilität eines globalen, aber es ist gut in der Klasse enthalten. Ich kann eine Methode aufrufen und jede Instanzvariable gleichzeitig mutieren, wenn ich möchte. Wenn ich das tue, bin ich verpflichtet, der Methode zumindest einen Namen zu geben, der klar macht, was es bedeutet, damit die Leute nicht überrascht sind, wenn dies passiert. Kommentare können ebenfalls helfen. Manchmal werden diese Kommentare als "Post-Bedingungen" formalisiert.

Der Nachteil funktionaler privater Methoden

Der funktionale Ansatz macht einige Abhängigkeiten klar. Wenn Sie nicht in einer reinen Funktionssprache sind, können versteckte Abhängigkeiten nicht ausgeschlossen werden. Wenn Sie nur eine Methodensignatur betrachten, wissen Sie nicht, dass sie im Rest des Codes keinen Nebeneffekt vor Ihnen verbirgt. Das tust du einfach nicht.

Post-Bedingungen

Wenn Sie und alle anderen im Team die Nebenwirkungen (Vor-/Nachbedingungen) zuverlässig in Kommentaren dokumentieren, ist der Gewinn aus dem funktionalen Ansatz viel geringer. Ja, ich weiß, träum weiter.

Schlussfolgerung

Persönlich tendiere ich in beiden Fällen zu funktionalen privaten Methoden, wenn ich kann, aber ehrlich gesagt liegt es hauptsächlich daran, dass diese Kommentare zu bedingten Nebenwirkungen vor/nach dem Eingriff keine Compilerfehler verursachen, wenn sie veraltet sind oder wenn Methoden nicht in der richtigen Reihenfolge aufgerufen werden. Wenn ich nicht wirklich die Flexibilität von Nebenwirkungen brauche, möchte ich lieber nur wissen, dass die Dinge funktionieren.

3
MagicWindow

Die erste Variante sieht für mich nicht intuitiv und potenziell gefährlich aus (stellen Sie sich vor, aus irgendeinem Grund macht jemand Ihre private Methode öffentlich).

Ich würde Ihre Variablen viel lieber bei der Klassenkonstruktion instanziieren oder als Argumente übergeben. Letzteres gibt Ihnen die Möglichkeit, funktionale Redewendungen zu verwenden und sich nicht auf den Status des enthaltenen Objekts zu verlassen.

1
Brian Agnew

Versuchen wir ein Beispiel, das etwas bewirkt. Verzeihen Sie mir, da dies Javascript ist, nicht Java. Der Punkt sollte der gleiche sein.

Besuchen Sie https://blockly-games.appspot.com/pond-duck?lang=de , klicken Sie auf die Registerkarte Javascript und fügen Sie Folgendes ein:

hunt = lock(-90,1), 
heading = -135;

while(true) {
  hunt()  
  heading += 2
  swim(heading,30)
}

function lock(direction, width) {
  var dir = direction
  var wid = width
  var dis = 10000

  //hunt
  return function() {
    //randomize() //Calling this here makes the state of dir meaningless
    scanLock()
    adjustWid()
    if (isSpotted()) {
      if (inRange()) {
        if (wid <= 4) {
          cannon(dir, dis)
        }
      }
    } else {
      if (!left()) {
        right()
      }
    }
  }

  function scanLock() {
    dis = scan(dir, wid);
  }

  function adjustWid() {
    if (inRange()) {
      if (wid > 1)
        wid /= 2;
    } else {
      if (wid < 16) {
        wid *= 2; 
      }
    }
  }

  function isSpotted() {
    return dis < 1000;
  }

  function left() {
    dir += wid;
    scanLock();
    return isSpotted();
  }

  function right() {
    dir -= wid*2;
    scanLock();
    return isSpotted()
  }

  function inRange() {
    return dis < 70;
  }

  function randomize() {
    dir = Math.random() * 360
  }
}

Sie sollten beachten, dass dir, wid und dis nicht oft herumgereicht werden. Sie sollten auch beachten, dass der Code in der Funktion, die lock() zurückgibt, dem Pseudocode sehr ähnlich sieht. Nun, es ist tatsächlicher Code. Trotzdem ist es sehr leicht zu lesen. Wir könnten Übergabe und Zuweisung hinzufügen, aber das würde Unordnung hinzufügen, die Sie im Pseudocode niemals sehen würden.

Wenn Sie argumentieren möchten, dass es in Ordnung ist, die Zuweisung und Übergabe nicht durchzuführen, da diese drei Variablen dauerhaft sind, sollten Sie ein Redesign in Betracht ziehen, das dir jeder Schleife einen zufälligen Wert zuweist. Nicht hartnäckig, oder?

Sicher, jetzt könnten wir dir in den Geltungsbereich fallen lassen, aber nicht ohne gezwungen zu sein, unseren pseudoähnlichen Code mit Übergabe und Einstellung zu überladen.

Nein, der Zustand ist nicht der Grund, warum Sie sich entscheiden, Nebenwirkungen zu verwenden oder nicht zu verwenden, anstatt zu bestehen und zurückzukehren. Nebenwirkungen allein bedeuten auch nicht, dass Ihr Code nicht lesbar ist. Sie erhalten nicht das Vorteile von reinem Funktionscode . Aber gut gemacht, mit guten Namen können sie tatsächlich schön zu lesen sein.

Das heißt nicht, dass sie sich nicht in einen Spaghetti wie einen Albtraum verwandeln können. Aber was kann dann nicht?

Fröhliche Entenjagd.

0
candied_orange

Es gibt bereits Antworten zum Objektstatus und wann die zweite Methode bevorzugt wird. Ich möchte nur einen allgemeinen Anwendungsfall für das erste Muster hinzufügen.

Das erste Muster ist vollkommen gültig, wenn Ihre Klasse nur kapselt einen Algorithmus. Ein Anwendungsfall hierfür ist, dass der Algorithmus zu groß wäre, wenn Sie ihn in eine einzelne Methode schreiben würden. Sie zerlegen es also in kleinere Methoden, machen es zu einer Klasse und machen die Untermethoden privat.

Das Übergeben des gesamten Status des Algorithmus über Parameter kann mühsam werden, sodass Sie die privaten Felder verwenden. Es stimmt auch mit der Regel im ersten Absatz überein, da es sich im Grunde um einen Zustand der Instanz handelt. Sie müssen nur bedenken und ordnungsgemäß dokumentieren, dass der Algorithmus nicht wiedereintrittsfähig ist, wenn Sie dafür private Felder verwenden . Dies sollte die meiste Zeit kein Problem sein, aber es kann Sie möglicherweise beißen.

0
Honza Brabec