Warum sollte ich keine unveränderlichen POJOs anstelle von JavaBeans verwenden?

79

Ich habe jetzt einige Java-Anwendungen implementiert, bisher nur Desktop-Anwendungen. Ich bevorzuge die Verwendung unveränderlicher Objekte zum Weitergeben der Daten in der Anwendung, anstatt Objekte mit Mutatoren (Setter und Getter ) zu verwenden, die auch als JavaBeans bezeichnet werden.

Aber in der Java-Welt scheint es viel üblicher zu sein, JavaBeans zu verwenden, und ich kann nicht verstehen, warum ich sie stattdessen verwenden sollte. Persönlich sieht der Code besser aus, wenn er nur unveränderliche Objekte behandelt, anstatt den Status ständig zu ändern.

Unveränderliche Objekte werden auch in Punkt 15 empfohlen : Veränderbarkeit minimieren , Effektives Java 2ed .

Wenn ich ein Objekt Personals JavaBean implementiert habe, würde es so aussehen:

public class Person {
    private String name;
    private Place birthPlace;

    public Person() {}

    public setName(String name) {
        this.name = name;
    }

    public setBirthPlace(Place birthPlace) {
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}

Und das gleiche Personimplementiert als unveränderliches Objekt:

public class Person {
    private final String name;
    private final Place birthPlace;

    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}

Oder näher an einem structin C:

public class Person {
    public final String name;
    public final Place birthPlace;

    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }
}

Ich könnte auch Getter im unveränderlichen Objekt haben, um die Implementierungsdetails zu verbergen. Aber da ich es nur als benutze struct, überspringe ich lieber die "Getter" und halte es einfach.

Ich verstehe einfach nicht, warum es besser ist, JavaBeans zu verwenden, oder ob ich mit meinen unveränderlichen POJOs weitermachen kann und sollte?

Viele der Java-Bibliotheken scheinen JavaBeans besser zu unterstützen, aber vielleicht wird mehr Unterstützung für unveränderliche POJOs mit der Zeit immer beliebter?

Jonas
quelle
1
Ich denke du meinst Punkt 15: Veränderbarkeit minimieren , nicht Unveränderlichkeit minimieren "
matt b
@matt: Danke =) Ich habe es jetzt aktualisiert.
Jonas
7
Ihr Unveränderlichkeitsansatz ist großartig und erspart Ihnen viele Thread-Probleme. 1
whiskeysierra
1
Sie würden einen Standardkonstruktor benötigen, wenn Sie JPA
Neil McGuigan

Antworten:

78

Bevorzugen Sie JavaBeans Wann

  • Sie müssen mit Umgebungen interagieren, die sie erwarten
  • Sie haben viele Eigenschaften, für die es unpraktisch wäre, alle Initialisierungen bei der Instanziierung durchzuführen
  • Sie haben einen Zustand, der aus irgendeinem Grund teuer oder unmöglich zu kopieren ist, aber eine Mutation erfordert
  • Sie denken, dass Sie irgendwann ändern müssen, wie auf Eigenschaften zugegriffen wird (z. B. Übergang von gespeicherten zu berechneten Werten, Zugriffsberechtigung usw.)
  • Sie möchten Codierungsstandards einhalten, die gedankenlos darauf bestehen, dass die Verwendung von JavaBeans "objektorientierter" ist

Bevorzugen Sie unveränderliche POJOs, wenn

  • Sie haben eine kleine Anzahl einfacher Eigenschaften
  • Sie müssen nicht mit Umgebungen interagieren, die JavaBean-Konventionen voraussetzen
  • Es ist einfach (oder zumindest möglich), den Status beim Klonen Ihres Objekts zu kopieren
  • Sie haben nie vor, das Objekt zu klonen
  • Sie sind sich ziemlich sicher, dass Sie den Zugriff auf Eigenschaften nie wie oben beschrieben ändern müssen
  • Es macht Ihnen nichts aus, zuzuhören, wie Ihr Code nicht ausreichend "objektorientiert" ist.
NUR MEINE RICHTIGE MEINUNG
quelle
+1 für die Auflistung und fast -1 für Ihren Namen :) Schließlich weiß jeder, dass meine Meinung richtig ist. Jeder wird natürlich im Sinne von Discworld definiert.
extraneon
12
Die Überprüfung der Grenzen und die Validierung der Eingaben können im Konstruktor erfolgen. Wenn die Eingabe ungültig oder nicht gebunden ist, möchte ich kein Objekt erstellen.
Jonas
24
Ihr zweiter Punkt bei Bohnen ist nicht zugunsten von Bohnen, sondern zugunsten des Builder-Musters. Sie können weiterhin ein unveränderliches Objekt erstellen.
4
Sie erwähnen nicht das Threading mit den unveränderlichen POJOs, was bei weitem einer der größten Gründe ist, sie zu verwenden. Ein weiteres wichtiges Element ist die Einfachheit: Wenn sich ein Objekt nicht ändert, ist es auf lange Sicht einfacher, damit zu arbeiten. Bei unveränderlichen POJOs müssen Sie sich im Allgemeinen keine Gedanken über das Klonen des Objekts machen. Sie können es einfach weitergeben.
Abschrecken
Wie erstellen Sie Objektdiagramme mit Zirkelverweisen, sodass diese Objekte unveränderlich sind? Ist das möglich? Zum Beispiel möchte ich, dass Klasse A und B unveränderlich sind, aber A braucht einen Verweis auf B und B braucht einen Verweis auf A. Gibt es eine Möglichkeit, dies zu tun?
Chakrit
39

Ich war überrascht, dass das Wort Thread in dieser Diskussion nirgendwo auftauchte.

Einer der Hauptvorteile unveränderlicher Klassen besteht darin, dass sie von Natur aus threadsicherer sind, da kein veränderlicher, gemeinsam genutzter Status vorliegt.

Dies erleichtert nicht nur die Codierung, sondern bietet Ihnen auch zwei Leistungsvorteile als Nebeneffekt:

  • Weniger Synchronisationsbedarf.

  • Mehr Spielraum für die Verwendung endgültiger Variablen, was spätere Compileroptimierungen erleichtern kann.

Ich versuche wirklich, mich eher unveränderlichen Objekten als JavaBean-Stilklassen zuzuwenden. Das Freilegen der Eingeweide von Objekten über Getter und Setter sollte wahrscheinlich nicht die Standardauswahl sein.

Benjamin Wootton
quelle
18

Nun, es hängt davon ab, was Sie versuchen zu tun. Wenn Sie mit einer dauerhaften Ebene arbeiten und eine Zeile aus der Datenbank in ein POJO abrufen und eine Eigenschaft ändern und zurückspeichern möchten, ist die Verwendung des JavaBean-Stils besser, insbesondere wenn Sie über viele Eigenschaften verfügen.

Bedenken Sie, dass Ihre Person viele Bereiche hat, wie Vor-, Mittel-, Nachname, Geburtsdatum, Familienmitglieder, Ausbildung, Job, Gehalt usw.

Und diese Person ist zufällig eine Frau, die gerade geheiratet hat und akzeptiert hat, dass ihr Nachname geändert wird, und Sie müssen die Datenbank aktualisieren.

Wenn Sie unveränderliches POJO verwenden, rufen Sie ein Personenobjekt ab, das sie darstellt, und erstellen ein neues Personenobjekt, an das Sie alle Eigenschaften, die Sie nicht geändert haben, sowie den neuen Nachnamen übergeben und speichern.

Wenn es sich um eine Java-Bean handelt, können Sie einfach setLastName () ausführen und speichern.

Es ist "Mutabilität minimieren" und nicht "Veränderbare Objekte niemals verwenden". Einige Situationen funktionieren besser mit veränderlichen Objekten. Es ist wirklich Ihre Aufgabe, zu entscheiden, ob ein veränderbares Objekt besser zu Ihrem Programm passt oder nicht. Sie sollten nicht immer sagen, dass unveränderliche Objekte verwendet werden müssen, sondern sehen, wie viele Klassen Sie unveränderlich machen können, bevor Sie anfangen, sich selbst zu verletzen.

Andrei Fierbinteanu
quelle
1
Ihr Beispiel ist jedoch fast nie das, was in "echten" Anwendungen getan werden muss. Normalerweise muss Ihr aktualisiertes Datenobjekt an ein Validierungsframework (Hibernate Validator, Spring Validator usw.) übergeben werden, in dem das Objekt verschiedenen Vorgängen unterzogen wird, die möglicherweise von verschiedenen Entwicklern geschrieben wurden. Wenn das Objekt veränderbar ist, wissen Sie nicht, ob Sie mit denselben Daten arbeiten, die übergeben wurden (wenn eine vor Ihrer Ausführung ausgeführte Validierung das Objekt an Ort und Stelle mutiert hat). Dies macht den Validierungsprozess weniger determinant, abhängiger von strenger Reihenfolge und unvergleichlich.
Schmied
Darüber hinaus verfügt eine "echte" Anwendung wahrscheinlich über einen speicherinternen oder verteilten Cache (EHCache usw.), der Ihre Domänenobjekte enthält. Wenn Sie wissen, dass Ihre Objekte unveränderlich sind, müssen Sie keinen Aufwand für das Kopieren beim Lesen verursachen. Sie können das In-Memory-Objekt sicher abrufen und verwenden, ohne sich um die Cache-Integrität sorgen zu müssen. IMHO, für Geschäftsdomänenobjekte dieser Art möchten Sie immer unveränderliche Objekte. Veränderbare Objekte sind für den lokalen Bereich und einige andere spezielle Fälle in Ordnung, in denen ich nicht die Charaktere habe, auf die ich hier eingehen kann.
Schmied
7

Zusammenfassend denke ich, dass:

  • Unveränderlichkeit erleichtert die Korrektheit (Strukturen können als Referenz übergeben werden und Sie wissen, dass nichts von einem fehlerhaften / böswilligen Client zerstört wird) und die Einfachheit des Codes
  • Mutabilität erleichtert die Homogenität : Spring und andere Frameworks erstellen ein Objekt ohne Argumente, legen Objekteigenschaften fest und voi là . Erleichtern Sie außerdem die Verwendung von Schnittstellen, indem Sie dieselbe Klasse zum Angeben von Daten und Speichern von Änderungen verwenden (Sie benötigen keine get(id): Clientund save(MutableClient)als MutableClient einige Nachkommen von Client.

Wenn es einen Zwischenpunkt gäbe (erstellen, Eigenschaften festlegen, unveränderlich machen), würden Frameworks möglicherweise eher einen unveränderlichen Ansatz fördern.

Wie auch immer, ich schlage vor, in unveränderlichen Objekten als "Nur-Lese-Java-Bohnen" zu denken und zu betonen, dass alles in Ordnung ist, wenn Sie ein guter Junge sind und diese gefährliche setProperty-Methode nicht berühren.

Helios
quelle
Ich würde zustimmen, dass das Beste aus beiden Welten darin besteht, dass Bibliotheken und Frameworks zu einem unveränderlichen Ansatz übergehen. Die Vorteile von unveränderlich sind inhärent, während die Vorteile von veränderlich zufällig sind.
Schmied
3

Ab Java 7 können Sie unveränderliche Bohnen haben, das Beste aus beiden Welten. Verwenden Sie die Annotation @ConstructorProperties für Ihren Konstruktor.

public class Person {
    private final String name;
    private final Place birthPlace;

    @ConstructorProperties({"name", "birthPlace"})
    public Person(String name, Place birthPlace) {
        this.name = name;
        this.birthPlace = birthPlace;
    }

    public String getName() {
        return name;
    }

    public Place getBirthPlace() {
        return birthPlace;
    }
}
Larry
quelle
1

Ich glaube nicht, dass unveränderliche Objekte so beliebt werden, um ehrlich zu sein.

Ich sehe die Vorteile, aber Frameworks wie Hibernate und Spring sind derzeit (und das aus gutem Grund) sehr im Trend und funktionieren am besten mit Bohnen.

Ich denke also nicht, dass Unveränderlichkeit schlecht ist, aber es würde Ihre Integrationsoptionen mit aktuellen Frameworks sicherlich einschränken.

BEARBEITEN Die Kommentare fordern mich auf, meine Antwort ein wenig zu klären.

Es gibt mit Sicherheit Problembereiche, in denen Unveränderlichkeit sehr nützlich ist und tatsächlich genutzt wird. Aber ich denke, der aktuelle Standard scheint veränderlich zu sein, da dies meistens erwartet wird und nur dann unveränderlich ist, wenn dies einen klaren Vorteil hat.

Und obwohl es in Spring tatsächlich möglich ist, Konstruktoren mit Argumenten zu verwenden, scheint es als eine Möglichkeit gedacht zu sein, Legacy- und / oder Code von Drittanbietern mit Ihrem wunderschönen brandneuen Spring-Code zu verwenden. Zumindest habe ich das aus der Dokumentation aufgegriffen.

Extraneon
quelle
2
Die Idee von IoC / DI basiert auf dem Einstellen / Injizieren des Zustands, weshalb Unveränderlichkeit im Frühjahr nicht so beliebt ist. Joda-Time ist jedoch ein gutes Beispiel für die unveränderliche Unveränderlichkeit.
Lunohodov
Sie können Konstruktorargumente verwenden, um Abhängigkeiten auch mit spring zu injizieren. Es scheint nur, dass dies nicht viele Leute tun (möglicherweise, weil es beängstigend ist, einen Konstruktor mit mehr als 10 Parametern zu haben, wenn Sie viele Abhängigkeiten haben).
Andrei Fierbinteanu
6
@lunohodov: Ich habe Google Guice für DI / IoC verwendet und es gibt kein Problem, eine Abhängigkeitsinjektion für den Konstruktor durchzuführen.
Jonas
Im Allgemeinen ist es am einfachsten, mit Dingen zu arbeiten (insbesondere in einer Multithread-Umgebung), wenn Daten mit unveränderlichen, einmal erstellten Verweisen auf veränderbare Objekte oder veränderlichen Verweisen auf unveränderliche Objekte gespeichert werden. Frei veränderbare Verweise auf veränderbare Objekte machen die Sache komplizierter. Beachten Sie, dass, wenn Xund Ysich auf dasselbe Objekt beziehen und man sich ändern möchte X.something, man die Identität von X behalten und ändern Y.somethingoder Y.somethingdie Identität von X unverändert lassen und ändern kann, aber die Identität von X und den Zustand von Y nicht beibehalten kann, während man sich ändert der Zustand von X.
Supercat
Diejenigen, die die Verwendung unveränderlicher Objekte vorantreiben, erkennen manchmal nicht die Bedeutung der Objektidentität und die Tatsache, dass veränderbare Objekte eine unveränderliche Identität auf eine Weise haben können, die unveränderliche Objekte nicht können.
Supercat
0

Unveränderlich in Bezug auf die Programmierung in Java: Etwas, das einmal erstellt wurde, sollte niemals einen erwarteten oder unerwarteten Statuswechsel haben!

Diese Technik ist nützlich bei der defensiven Programmierung, bei der eine andere Entität keine Zustandsänderung hervorrufen kann.

Beispiele, bei denen Sie keine Änderung erwarten: Externe Systeme (Single- oder Multi-Threaded), die von Ihrer Ebene referenziert werden und auf das Objekt einwirken und es wissentlich oder unwissentlich ändern. Jetzt kann es sich um ein POJO oder eine Sammlung oder eine Objektreferenz handeln, und Sie erwarten keine Änderung daran oder möchten die Daten schützen. Sie würden das Objekt definitiv als Verteidigungstechnik unveränderlich machen

Beispiele, bei denen Sie Änderungen erwarten: Sie brauchen wirklich keine Unveränderlichkeit, da dies die richtigen Programmierverfahren behindert.

Rohitdev
quelle