Verwenden Sie öffentliche Finale anstatt private Getter

51

Ich sehe die unveränderlichsten POJOs so geschrieben:

public class MyObject {
    private final String foo;
    private final int bar;

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

    public String getFoo() {
        return foo;
    }

    public int getBar() {
        return bar;
    }
}

Dennoch neige ich dazu, sie so zu schreiben:

public class MyObject {
    public final String foo;
    public final int bar;

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

Beachten Sie, dass die Referenzen endgültig sind, sodass das Objekt noch unveränderlich ist. Dadurch kann ich weniger Code schreiben und kürzeren Zugriff (mit 5 Zeichen: getund ()).

Der einzige Nachteil, den ich sehen kann, ist, dass Sie die Implementierung von getFoo()Down-the-Road ändern möchten, um etwas Verrücktes zu tun, was Sie nicht können. Realistisch gesehen geschieht dies jedoch nie, weil das Objekt unveränderlich ist. Sie können dies während der Instantiierung überprüfen, unveränderliche Verteidigungskopien während der Instantiierung erstellen (siehe ImmutableListz. B. Guave ) und die foooder bar-Objekte für den getAnruf vorbereiten .

Gibt es irgendwelche Nachteile, die ich vermisse?

BEARBEITEN

Ich vermute, ein weiterer Nachteil, den ich vermisse, ist die Verwendung von Serialisierungsbibliotheken mit Reflektion über Methoden, die mit getoder beginnen is, aber das ist eine ziemlich schreckliche Praxis ...

Cory Kendall
quelle
Was zur Hölle? finalmacht eine Variable nicht zum unveränderlichen Objekt. Normalerweise verwende ich ein Design, in dem ich finalvor dem Erstellen eines Rückrufs Felder definiere , damit der Rückruf auf diese Felder zugreifen kann. Es können natürlich alle Methoden einschließlich aller Methoden aufgerufen setXwerden.
Tomáš Zato
@ TomášZato gibt es keine setX Methoden auf String, intoder MyObject. In der ersten Version finalsoll nur sichergestellt werden, dass andere Methoden als der Konstruktor innerhalb der Klasse dies beispielsweise nicht versuchen bar = 7;. In der zweiten Version finalist notwendig , die Verbraucher zu verhindern , dass zu tun: MyObject x = new MyObject("hi", 5); x.bar = 7;.
Cory Kendall
1
Nun, Ihr " Beachten Sie, dass die Verweise endgültig sind, das Objectist also immer noch unveränderlich. " Ist irreführend final Object. Entschuldigung für das Missverständnis.
Tomáš Zato
3
Wenn die zugrunde liegenden Werte veränderlich wären, würde die private + Accessor-Straße dies ebenfalls nicht verhindern - myObj.getFoo().setFrob(...).
Beni Cherniavsky-Paskin

Antworten:

38

Vier Nachteile, die ich mir vorstellen kann:

  1. Wenn Sie eine schreibgeschützte und veränderbare Form derselben Entität haben möchten, besteht ein gemeinsames Muster darin, eine unveränderbare Klasse Entity zu haben, die nur Accessoren mit geschützten Membervariablen verfügbar macht, und dann eine MutableEntity zu erstellen, die sie erweitert und Setter hinzufügt. Ihre Version verhindert es.
  2. Die Verwendung von Gettern und Setzern entspricht der JavaBeans-Konvention. Wenn Sie Ihre Klasse als Bean in eigenschaftsbasierten Technologien wie JSTL oder EL verwenden möchten, müssen Sie öffentliche Getter verfügbar machen.
  3. Wenn Sie jemals die Implementierung ändern möchten, um die Werte abzuleiten oder in der Datenbank nachzuschlagen, müssen Sie den Clientcode umgestalten. Mit einem Accessor / Mutator-Ansatz können Sie nur die Implementierung ändern.
  4. Am wenigsten verwundert: Wenn ich öffentliche Instanzvariablen sehe, suche ich sofort nach Personen, die sie möglicherweise mutieren, und befürchte, dass ich die Büchse der Pandora öffne, weil die Kapselung verloren geht. http://en.wikipedia.org/wiki/Principle_of_least_astonishment

Das heißt, Ihre Version ist definitiv prägnanter. Wenn dies eine spezialisierte Klasse wäre, die nur innerhalb eines bestimmten Pakets verwendet wird (vielleicht ist der Umfang eines Pakets hier eine gute Idee), dann könnte ich dies für einmalige Fälle in Betracht ziehen. Aber ich würde keine wichtigen APIs wie dieses verfügbar machen.

Brandon
quelle
3
Tolle Antworten; Ich bin mit einigen nicht einverstanden, bin mir aber nicht sicher, ob diese Seite das beste Diskussionsforum ist. Kurz gesagt: 1) Ich kann andere brechen, indem ich die veränderbare Form verwende, bei der sie annehmen, dass sie unveränderlich ist (vielleicht sollte ich der Klasse in meinen Beispielen ein Finale hinzufügen). 2) Ich stimme zu. 3) Ich würde argumentieren, dass dies kein POJO mehr ist case, 4) Ich stimme vorerst zu, aber hoffentlich können Diskussionen wie diese das am wenigsten Erstaunliche ändern :).
Cory Kendall
2
5) Sie können inhärent veränderbare Klassen / Typen - wie Arrays - nicht sicher verfügbar machen. Der sichere Weg ist, jedes Mal eine Kopie von einem Getter zurückzugeben.
Clockwork-Muse
@ Clockwork-Muse kannst du, wenn du annimmst, dass Leute keine Idioten sind. Wenn Sie auf someObj.thisList.add () zugreifen, ist es offensichtlich, dass Sie den Status einiger anderer Objekte ändern. Mit someObj.getThisList (). Add () ist es unbekannt. Sie müssen nach der Dokumentation fragen oder die Quelle nachschlagen, aber allein anhand der Methodendeklaration ist es unmöglich zu sagen.
Kritzikratzi
2
@kritzikratzi - Das Problem ist, dass Sie nicht garantieren können, dass Ihr Code nicht in Idioten gerät. An einem gewissen Punkt es wird (das könnte sogar sein , sich am Ende!), Und die Überraschung könnte ein großes Problem sein.
Clockwork-Muse
1
Es ist von Natur aus falsch , einen veränderlichen Typ von einem unveränderlichen Typ erben zu lassen. Denken Sie daran, dass ein unveränderlicher Typ die Garantie "Ich ändere mich nicht. Immer." Gibt.
Deduplizierer
26

Werde auch die Getter / Setter los und es geht dir gut!

Dies ist ein sehr kontroverses Thema unter Java-Programmierern.

Wie auch immer, es gibt zwei Situationen, in denen ich öffentliche Variablen anstelle (!) Von Gettern / Setzern verwende:

  1. öffentliches Finale Für mich bedeutet dies "Ich bin unveränderlich" viel besser als nur ein Getter. Die meisten IDEs geben den letzten Modifikator während der automatischen Vervollständigung mit einem 'F' an. Anders als bei Gettern / Setzern, bei denen nach dem Fehlen eines setXXX gesucht werden muss.
  2. public non-final Ich liebe dies für Datenklassen. Ich mache nur alle Felder öffentlich zugänglich. Keine Getter, Setter, Konstrukteure. Nein, nichts. Weniger als ein Pojo. Für mich bedeutet dies sofort: "Schau, ich bin dumm. Ich habe Daten, das ist alles. Es ist DEINE Aufgabe, die richtigen Daten in mich zu stecken." Gson / JAXB / etc. behandeln diese Klassen ganz gut. Es ist eine Wonne zu schreiben. Es gibt keinen Zweifel an ihrem Zweck oder ihren Fähigkeiten. Und das Wichtigste: Sie wissen, dass es keine Nebenwirkungen gibt, wenn Sie eine Variable ändern. IMHO führt dies zu sehr präzisen Datenmodellen mit wenigen Mehrdeutigkeiten, wohingegen Getter und Setter dieses große Problem haben, bei dem manchmal Magie in ihnen vorkommt.
kritzikratzi
quelle
5
Es ist schön, ab und zu etwas Vernunft zu sehen :)
Navin,
2
Bis der Tag kommt, an dem sich Ihr Modell entwickelt und eines der alten Felder von einem anderen abgeleitet werden kann. Da Sie die Abwärtskompatibilität aufrechterhalten möchten, muss das alte Feld erhalten bleiben. Anstatt nur den Get- und Setter-Code zu ändern, müssten Sie das Feld überall ändern, wo dieses alte Feld verwendet wurde, um sicherzustellen, dass es der neuen Modelldarstellung folgt. Einfach zu schreiben. Ja sicher. Langfristig zu wartender Alptraum. Deshalb haben wir Getter / Setter oder besser noch in C # -Eigenschaftenzugriffen.
Newtopian
9

Mit den Worten des Laien:

  • Sie verletzen die Kapselung , um einige Codezeilen zu sparen. Das vereitelt den Zweck von OOD.
  • Der Client-Code ist fest an die Namen Ihrer Klassenmitglieder gebunden. Die Kopplung ist schlecht. Der gesamte Zweck von OOD besteht darin, eine Kopplung zu verhindern.
  • Sie sind sich auch sehr sicher, dass Ihre Klasse niemals wandelbar sein muss. Dinge ändern sich. Veränderung ist das einzige, was konstant ist.
Tulains Córdova
quelle
6
Wie ist dies eine Verletzung der Kapselung? Beide Versionen sind der Außenwelt gleichermaßen ausgesetzt (mit Lesezugriff). Sie haben Recht mit der harten Kopplung und Unflexibilität von Veränderungen, aber das werde ich nicht bestreiten.
KChaloux
4
@KChaloux Sie verwechseln "Encapsulation Violation" mit "Code kann nicht kompiliert werden und muss Zeit damit verschwenden, ihn zu reparieren". Das zweite passiert später. Um es deutlich zu machen: Die Kapselung ist bereits in der Gegenwart verletzt. Die Innereien Ihrer Klasse sind in der heutigen Zeit nicht mehr gekapselt. Der Client-Code wird jedoch in Zukunft beschädigt, wenn sich die Klasse in der Zukunft ändert, da die Kapselung in der Vergangenheit beschädigt wurde. Es gibt zwei verschiedene "Unterbrechungen": Kapselung heute und Client-Code morgen, da keine Kapselung vorliegt.
Tulains Córdova
8
Technisch gesehen, wenn Sie Getter / etc. Der Client-Code wird stattdessen fest an Methodennamen gekoppelt. Sehen Sie sich an, wie viele Bibliotheken alte Versionen von Methoden mit einem "veralteten" Tag / einer "veralteten" Annotation enthalten müssen, da älterer Code immer noch von ihnen abhängt (und manche Leute verwenden sie möglicherweise immer noch, weil sie es einfacher finden, mit ihnen zu arbeiten, Sie aber nicht soll das machen).
JAB
5
Pragmatisch: Wie oft überarbeiten Programmierer tatsächlich private Feldnamen, ohne die öffentlichen Getter / Setter umzubenennen? Soweit ich weiß, gibt es eine gemeinschaftsweite Konvention, um sie gleich zu benennen, auch wenn diese Konvention möglicherweise nur ein Ergebnis von IDE-Tools ist, die aus Feldnamen Getter und Setter generieren. Theoretisch: Aus objektorientierter Modellierungsperspektive ist jedoch alles Öffentliche Teil des öffentlichen Auftrags und kein internes Implementierungsdetail. Wenn also jemand beschließt, ein letztes Feld zu veröffentlichen, sagt er dann nicht, dass dies der Vertrag ist, den er zu erfüllen verspricht?
Thomas Jung
3
@ user949300 Sag mir welche, damit ich lernen kann.
Tulains Córdova
6

Ein möglicher Nachteil, den ich sofort sehe, ist, dass Sie an die interne Darstellung der Daten in der Klasse gebunden sind. Dies ist wahrscheinlich keine große Sache, aber wenn Sie die Setter verwenden und entscheiden, dass foo und bar von einer anderen Klasse zurückgegeben werden, die an einer anderen Stelle definiert wurde, müssen die Klassen, die MyObject verbrauchen, nicht geändert werden. Sie müssten nur MyObject berühren. Wenn Sie jedoch die nackten Werte verwenden, müssen Sie Ihr MyObject, das ich verwendet habe, überall anfassen.

ipaul
quelle