Sollten die Methoden einer Klasse ihre eigenen Getter und Setter aufrufen?

53

Wo ich arbeite, sehe ich viele Klassen, die solche Dinge tun:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

Wenn ich das von Grund auf neu geschrieben hätte, hätte ich methodWithLogic()stattdessen folgendes geschrieben :

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

Ich glaube, wenn die Klasse ihre eigenen Getter und Setter aufruft, ist der Code schwieriger zu lesen. Für mich bedeutet das fast , dass in diesem Methodenaufruf komplexe Logik vor sich geht, obwohl dies in unserem Fall so gut wie nie der Fall ist. Wenn ich unbekannten Code debugge, wer sagt dann, dass der Fehler bei dieser Methode keine Nebenwirkung hat? Mit anderen Worten, ich muss auf dem Weg zum Verständnis des Codes viele Abstecher machen.

Gibt es Vorteile für die erste Methode? Ist die erste Methode eigentlich besser?

Daniel Kaplan
quelle
Wenn ich unbekannten Code debugge, wer sagt dann, dass der Fehler bei dieser Methode keine Nebenwirkung hat? Ihre Unit-Tests. :)
Joshua Taylor
1
Durch die Verwendung von Gettern / Settern in Ihrem internen Code können Sie einen Haltepunkt in Ihrem Code erstellen, wenn Sie ihn mit einem Debugger durchlaufen. Sie können damit auch sicherstellen, dass Sie wissen, dass das Festlegen / Abrufen eines Werts auf diese Methode und nicht auf einen ungültigen Zugriff zurückzuführen ist, wenn beim Festlegen ein Fehler auftritt. Insgesamt sorgt es für mehr Konsistenzcode.
Callyalater

Antworten:

46

Ich werde nicht sagen, was besser oder schlechter ist, weil das teilweise von Ihrer Situation abhängt (meiner Meinung nach). Bedenken Sie jedoch, dass Ihre Getter und Setter die Implementierung später möglicherweise ändern. Wenn Sie diese umgehen, wird diese Logik übersprungen.

Was passiert zum Beispiel, wenn Sie später einigen Setterfeldern ein "Dirty" -Flag hinzufügen? Wenn Sie Ihre Setter in Ihrem internen Code anrufen, setzen Sie das Dirty-Flag, ohne einen anderen Code zu ändern. In vielen Situationen wäre dies eine gute Sache.

Matt S
quelle
Aber was ist, wenn Sie die Daten im Backend initialisieren und nicht möchten, dass sie als schmutzig interpretiert werden? Wenn Sie setField()von Anfang an angerufen haben, haben Sie gerade einen Fehler gemeldet.
Daniel Kaplan
6
Richtig, deshalb sage ich, dass es teilweise von der Situation abhängt. Möglicherweise sollte Ihre Dateninitialisierung die Setter überspringen, der andere logische Code jedoch nicht. Wählen Sie geeignete Regeln aus und halten Sie sich daran.
Matt S
3
@DanielKaplan: Der Punkt ist, dass das Aufrufen von Gettern und Setzern in den meisten Fällen das Richtige ist und nur in bestimmten Situationen nicht. Wenn Sie jedoch im Code der realen Welt eine vorhandene Get- / Setter-Implementierung später ändern, um beabsichtigte Nebenwirkungen einzuführen, müssen Sie höchstwahrscheinlich jeden Aufruf des Get- oder Setters in der Klasse und auch jeden direkten Zugriff auf das überprüfen Feld. Deshalb sollten Sie versuchen, Ihre Klassen so klein wie möglich zu halten.
Doc Brown
33

Ein direkter Anruf beim Setter ist an sich kein Problem. Die Frage sollte wirklich lauten: Warum hat unser Code überall Setter?

Veränderbare Klassen sind ein gefährliches Spiel, insbesondere wenn die Klasse selbst ihren eigenen Zustand verwaltet. Überlegen Sie, wie viele dieser Setter wirklich existieren müssen. Wie viele können im Konstruktor festgelegt werden und werden dann vollständig von der Klasse selbst eingekapselt?

Wenn das Feld extern einstellbar sein muss, fragen Sie sich, ob die Methode mit Logik vorhanden sein soll. Ist Ihre Klasse selbst wirklich nur eine Daten- / Statusklasse? Hat diese Klasse mehr als eine Verantwortung?

Versteht mich nicht falsch, es wird Fälle geben, in denen dies in Ordnung ist. Ich sage nicht, dass Sie Setter auf Klassen mit Logik vollständig beseitigen sollten. Dies sollte jedoch die Ausnahme und nicht die Regel sein. Und in diesen wenigen Fällen sollte es offensichtlich sein, auf welche direkt zugegriffen werden kann.

pdr
quelle
1
Ich stimme voll und ganz zu / übe diese Denkweise. Ich denke auch, dass Sie einen Punkt ansprechen, der in der Tat wichtiger ist als meine Frage. Trotzdem habe ich nicht das Gefühl, meine ursprüngliche Frage direkt zu beantworten. Es sei denn, Sie sagen, "im großen Schema der Dinge spielt Ihre Frage keine Rolle". Das kann auch wahr sein :)
Daniel Kaplan
@ DanielKaplan: Ich sage genau das. :) Ich war zwischen Antwort und Kommentar hin und her gerissen, aber ich glaube wirklich nicht, dass es eine richtige Antwort gibt, die die größere Frage nicht stellt.
pdr
9

Ja, die Methoden Ihrer Klasse sollten die Getter und Setter aufrufen. Der springende Punkt beim Schreiben von Gettern und Setzern ist die Zukunftssicherheit. Sie können jede Eigenschaft zu einem Feld machen und die Daten den Benutzern der Klasse direkt zur Verfügung stellen. Der Grund, warum Sie die Getter und Setter erstellen, liegt nicht unbedingt in der komplexen Logik, sondern darin, dass die Schnittstelle in Zukunft nicht kaputt geht, wenn Sie sie hinzufügen müssen.

Michael
quelle
1
Ich sehe die Beziehung zwischen Ihrem ersten Satz und dem Rest Ihres Absatzes nicht. Der erste Satz besagt, dass eine Klasse ihre eigenen Getter und Setter nennen sollte. Der Rest des Abschnitts erklärt, warum der Client-Code (dh der Code, der die Klasse verwendet) die Getter und Setter verwenden sollte, anstatt direkt auf die Felder zuzugreifen. Aber ich sehe keinen Grund, warum die Klasse nicht direkt auf ihre eigenen Felder zugreifen sollte .
Daniel Kaplan
1
@DanielKaplan Mein Punkt ist, dass die Gründe ein und dasselbe sind. Wenn Sie später Setter-Logik hinzufügen, wirkt sich dies genauso (potenziell) auf den internen Code aus wie auf den externen.
Michael
2
@Michael: Es kann oft gute Gründe für eine Klasse geben, keine eigenen Getter / Setter zu verwenden. Ein häufiges Szenario besteht darin, dass es viele Setter in der Form gibt. someField=newValue; refresh(); Wenn die Methode das Festlegen mehrerer Felder zulässt, würde der Aufruf von Setters zum Schreiben dieser Felder redundante "Aktualisierungs" -Operationen verursachen. Wenn Sie alle Felder schreiben und dann refresh()einmal aufrufen , kann dies zu einem effizienteren und reibungsloseren Betrieb führen.
Supercat
6

Um Ihre Fragen in einem Wort zu beantworten, ja.

Wenn eine Klasse ihre eigenen Getter und Setter aufruft, erhöht dies die Erweiterbarkeit und bietet eine bessere Basis für zukünftigen Code.

Sagen Sie, Sie haben so etwas:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

Wenn Sie die Setter für Jahr und Marke aufrufen, werden möglicherweise keine Funktionen hinzugefügt. Was ist jedoch, wenn Sie den Settern eine Eingabevalidierung hinzufügen möchten?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}
Zach Latta
quelle
5

Eine Sache, die nicht erwähnt wurde, ist, dass Getter und Setter (wie alle Methoden) in Java virtuell sind. Dies fügt eine weitere Funktion hinzu, um sie immer in Ihrem Code zu verwenden. Ein anderer Benutzer könnte Ihre Klasse erweitern und Ihre Getter und Setter überschreiben. Ihre Basisklasse würde dann die Daten der Unterklasse anstelle ihrer eigenen verwenden. In einer Sprache, in der Sie virtuelle Funktionen explizit markieren, ist dies sehr viel praktischer, da Sie vorhersagen und deklarieren können, mit welchen Funktionen dies durchgeführt werden kann. In Java müssen Sie sich dessen immer bewusst sein. Wenn es erwünscht ist, ist es eine gute Sache, sie in Ihrem Code zu verwenden, ansonsten nicht so sehr.

Jonathan Henson
quelle
3

Wenn Sie versuchen, etwas zu erreichen, das von der öffentlichen Schnittstelle bereitgestellt wird, verwenden Sie die Getter / Setter.

Als Eigentümer / Entwickler der Klasse dürfen Sie jedoch auf die privaten Bereiche Ihres Codes zugreifen (innerhalb der Klasse natürlich), aber Sie sind auch dafür verantwortlich, die Gefahren zu mindern.

Vielleicht haben Sie einen Getter, der eine interne Liste durchläuft, und Sie möchten den aktuellen Wert abrufen, ohne den Iterator zu erhöhen. Verwenden Sie in diesem Fall die private Variable.

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}
ConditionRacer
quelle
Können Sie die Gründe dafür nennen?
Daniel Kaplan
1

Ja. Getter und Setter stellen den Status dar. Drehen Sie also diese Frage um. Möchten Sie mehrere Möglichkeiten verfolgen, den Status eines Objekts auf dieselbe Weise zu ändern, sofern dies nicht erforderlich ist?

Je weniger Dinge Sie im Auge behalten müssen, desto besser ist es, mit unveränderlichen Objekten leichter umzugehen.

Bedenken Sie, dass nichts Sie daran hindert, sowohl ein öffentliches Feld als auch einen öffentlichen Getter / Setter zu haben - aber was würden Sie gewinnen?

Es kann gelegentlich wünschenswert oder sogar notwendig sein, aus einem oder mehreren Gründen direkt auf das Feld zuzugreifen. Sie sollten sich davor nicht scheuen, wenn es passiert. Aber Sie sollten es wirklich nur tun, wenn dies einen definierten Nutzen hat.

jmoreno
quelle
Der Punkt meiner Frage ist, dass ich nur nach dem direkten Zugriff auf die Felder in derselben Klasse frage. Ich verstehe bereits den Zweck von Gettern und Setzern für Client-Code.
Daniel Kaplan
@DanielKaplan: Ich verstehe das und was ich sage ist, dass du sie so weit wie möglich gleich behandeln solltest.
Jmoreno
@DanielKaplan: Sie könnten einen privaten Setter und einen öffentlichen Setter haben, die genau das gleiche Ergebnis (alle Nebenwirkungen gleich) haben, zumindest für den Moment, aber was würde Ihnen das bringen, abgesehen von der Möglichkeit, dass die Dinge auseinander gehen? Der Unterschied zwischen diesem Szenario und dem, was Sie beschreiben, besteht darin, dass Sie nicht vermeiden können , auf den Status außerhalb der Getter / Setter zuzugreifen, aber Sie können dies tatsächlich vermeiden. Komplizieren Sie Ihren Code nicht, es sei denn, Sie müssen.
Jmoreno
1

Beide Methoden haben ihre Anwendungsfälle. Da der öffentliche Setter den Feldwert (und / oder die gebundenen Werte) konsistent hält, sollten Sie den Setter verwenden, wenn Ihre Methodenlogik diese Konsistenzlogik nicht beeinträchtigt. Wenn Sie nur Ihre "Eigenschaft" festlegen, verwenden Sie Setter. Auf der anderen Seite gibt es Situationen, in denen Sie direkten Zugriff auf einige Felder benötigen, z. B. Massenvorgänge mit Heavy Setter oder Vorgänge, bei denen das Setter-Konzept zu einfach ist.

Es liegt in Ihrer Verantwortung, für Einheitlichkeit zu sorgen. Setter tut dies per Definition, kann jedoch keine komplexen Fälle abdecken.

Artur
quelle
1

Ich würde nein sagen ..

Wenn Ihre Getter / Setter nur abrufen und festlegen (keine verzögerte Initialisierung, keine Überprüfungen usw.), dann verwenden Sie einfach Ihre Variablen. Wenn Sie in Zukunft Ihre Getter / Setter ändern, können Sie sehr gut Ihre ändern methodWithLogic()(weil sich Ihre Klasse ändert), und Sie können einen Getter / Setter anstelle einer direkten Zuweisung aufrufen. Sie können diesen Aufruf für den Getter / Setter dokumentieren (da es ungewöhnlich ist, einen Getter / Setter aufzurufen, wenn der Rest Ihres Klassencodes direkt die Variable verwendet).

Die JVM leitet häufige Anrufe an Getter / Setter weiter. Es ist also nie ein Leistungsproblem. Der Vorteil der Verwendung von Variablen ist die Lesbarkeit und meiner Meinung nach würde ich mich dafür entscheiden.

Hoffe das hilft..

Pravin Sonawane
quelle