Wenn unveränderliche Objekte¹ gut und einfach sind und Vorteile bei der gleichzeitigen Programmierung bieten, warum erstellen Programmierer dann immer wieder veränderbare Objekte²?
Ich habe vier Jahre Erfahrung in der Java-Programmierung und aus meiner Sicht ist das erste, was Leute nach dem Erstellen einer Klasse tun,, Getter und Setter in der IDE zu generieren (und sie somit veränderbar zu machen). Gibt es einen Mangel an Bewusstsein oder können wir in den meisten Szenarien nicht mit veränderlichen Objekten umgehen?
¹ Unveränderliches Objekt ist ein Objekt, dessen Status nach seiner Erstellung nicht mehr geändert werden kann.
² Ein veränderbares Objekt ist ein Objekt, das nach seiner Erstellung geändert werden kann.
object-oriented
immutability
ahsteele
quelle
quelle
Antworten:
Sowohl veränderbare als auch unveränderbare Objekte haben ihre eigenen Verwendungen, Vor- und Nachteile.
Unveränderliche Gegenstände machen das Leben in vielen Fällen tatsächlich einfacher. Sie eignen sich insbesondere für Werttypen, bei denen Objekte keine Identität haben, sodass sie leicht ersetzt werden können. Und sie können die gleichzeitige Programmierung sicherer und sauberer machen (die meisten der notorisch schwer zu findenden Fehler im Zusammenhang mit der Parallelität werden letztendlich durch den veränderlichen Status verursacht, der von den Threads gemeinsam genutzt wird). Bei großen und / oder komplexen Objekten kann das Erstellen einer neuen Kopie des Objekts für jede einzelne Änderung sehr kostspielig und / oder mühsam sein. Bei Objekten mit einer eindeutigen Identität ist das Ändern vorhandener Objekte viel einfacher und intuitiver als das Erstellen einer neuen, geänderten Kopie.
Denken Sie an eine Spielfigur. In Spielen hat Geschwindigkeit oberste Priorität. Wenn Sie Ihre Spielcharaktere also mit veränderlichen Objekten darstellen, wird Ihr Spiel höchstwahrscheinlich erheblich schneller ausgeführt als bei einer alternativen Implementierung, bei der bei jeder kleinen Änderung eine neue Kopie des Spielcharakters erstellt wird.
Darüber hinaus basiert unsere Wahrnehmung der realen Welt unweigerlich auf veränderlichen Objekten. Wenn Sie Ihr Auto an der Tankstelle mit Kraftstoff tanken, nehmen Sie es die ganze Zeit über als dasselbe Objekt wahr (dh seine Identität bleibt erhalten, während sich sein Zustand ändert) - nicht als ob das alte Auto mit leerem Tank durch ein konsekutives neues ersetzt worden wäre Autoinstanzen, deren Panzer nach und nach immer voller wird. Wenn wir also eine reale Domäne in einem Programm modellieren, ist es normalerweise einfacher und unkomplizierter, das Domänenmodell mit veränderlichen Objekten zu implementieren, um reale Entitäten darzustellen.
Abgesehen von all diesen legitimen Gründen ist die Trägheit des Geistes, auch bekannt als Widerstand gegen Veränderungen, leider die wahrscheinlichste Ursache, warum Menschen ständig veränderliche Objekte erschaffen. Beachten Sie, dass die meisten Entwickler von heute lange bevor die Unveränderlichkeit (und das darin enthaltene Paradigma, die funktionale Programmierung) in ihrem Einflussbereich "trendy" wurde, geschult wurden und ihr Wissen über neue Werkzeuge und Methoden unseres Handels nicht auf dem neuesten Stand halten - Tatsächlich widersetzen sich viele von uns Menschen positiv neuen Ideen und Prozessen. "Ich programmiere seit nn Jahren so und mir sind die neuesten dummen Trends egal!"
quelle
Point
Klasse in .NET ist zum Beispiel unveränderlich, aber das Erstellen neuer Punkte als Ergebnis von Änderungen ist einfach und daher erschwinglich. Das Animieren eines unveränderlichen Charakters kann durch Entkoppeln der "beweglichen Teile" sehr billig gemacht werden (aber ja, ein Aspekt ist dann veränderlich). (2) „große und / oder komplexe Objekte“ können sehr wohl unveränderlich sein. Saiten sind oft groß und profitieren normalerweise von der Unveränderlichkeit. Ich habe einmal eine komplexe Graph-Klasse umgeschrieben, um unveränderlich zu sein und den Code einfacher und effizienter zu machen. In solchen Fällen ist ein veränderlicher Builder der Schlüssel.Ich denke, Sie haben alle die offensichtlichste Antwort verpasst. Die meisten Entwickler erstellen veränderbare Objekte, da die Veränderbarkeit in imperativen Sprachen der Standard ist. Die meisten von uns haben etwas Besseres mit ihrer Zeit zu tun, als den Code ständig von den Standardeinstellungen zu entfernen - richtiger oder nicht. Und Unveränderlichkeit ist kein Allheilmittel mehr als irgendein anderer Ansatz. Es macht einige Dinge einfacher, andere jedoch viel schwieriger, wie einige Antworten bereits gezeigt haben.
quelle
final
,const
nimmt usw. ein bisschen mehr Mühe ... es sei denn , Sie eine Codevorlage :-) einrichtenEs gibt einen Platz für die Veränderbarkeit. Domänengesteuerte Entwurfsprinzipien bieten ein solides Verständnis dafür, was veränderlich und was unveränderlich sein sollte. Wenn Sie darüber nachdenken, werden Sie feststellen, dass es unpraktisch ist, sich ein System vorzustellen, in dem jede Änderung des Zustands eines Objekts dessen Zerstörung und Neuzusammensetzung erfordert, sowie jedes Objekt, das darauf verweist. Bei komplexen Systemen kann dies leicht dazu führen, dass der Objektgraph des gesamten Systems vollständig gelöscht und neu erstellt wird
Die meisten Entwickler machen nichts, wo die Leistungsanforderungen so bedeutend sind, dass sie sich auf die Parallelität konzentrieren müssen (oder auf viele andere Probleme, die von den Informierten allgemein als gute Praxis angesehen werden).
Es gibt einige Dinge, die Sie mit unveränderlichen Objekten einfach nicht tun können, z. B. bidirektionale Beziehungen. Sobald Sie einen Zuordnungswert für ein Objekt festgelegt haben, ändert sich dessen Identität. Sie setzen also den neuen Wert für das andere Objekt und dieser ändert sich ebenfalls. Das Problem ist, dass die Referenz des ersten Objekts nicht mehr gültig ist, da eine neue Instanz erstellt wurde, um das Objekt mit der Referenz darzustellen. Wenn Sie dies fortsetzen, würde dies nur zu unendlichen Regressionen führen. Nachdem ich Ihre Frage gelesen hatte, habe ich eine kleine Fallstudie durchgeführt. So sieht es aus. Haben Sie einen alternativen Ansatz, der eine solche Funktionalität bei gleichzeitiger Aufrechterhaltung der Unveränderlichkeit ermöglicht?
quelle
Relationship(a, b)
; Zum Zeitpunkt der Erstellung der Beziehung sind beide Entitätena
&b
bereits vorhanden, und die Beziehung selbst ist ebenfalls unveränderlich. Ich sage nicht, dass dieser Ansatz in Java praktisch ist. nur dass es möglich ist.R
dasRelationship(a,b)
und beides ista
undb
unveränderlich ist, würde weder ein Verweis daraufa
nochb
ein Verweis darauf enthaltenR
. Damit dies funktioniert, müsste die Referenz an einer anderen Stelle gespeichert werden (z. B. in einer statischen Klasse). Verstehe ich Ihre Absicht richtig?Ich habe "rein funktionale Datenstrukturen" gelesen und festgestellt, dass es einige Datenstrukturen gibt, die mit veränderlichen Objekten viel einfacher zu implementieren sind.
Um einen binären Suchbaum zu implementieren, müssen Sie jedes Mal einen neuen Baum zurückgeben: Ihr neuer Baum musste eine Kopie von jedem Knoten erstellen, der geändert wurde (die nicht geänderten Zweige werden gemeinsam genutzt). Für Ihre Einfügefunktion ist das nicht so schlimm, aber für mich wurden die Dinge ziemlich schnell ineffizient, als ich anfing, an Löschen und Neuausgleichen zu arbeiten.
Die andere Sache, die Sie beachten müssen, ist, dass Sie jahrelang objektorientierten Code schreiben und nie wirklich erkennen können, wie schrecklich der gemeinsam genutzte veränderbare Zustand sein kann, wenn Ihr Code nicht auf eine Weise ausgeführt wird, die Parallelitätsprobleme aufdeckt.
quelle
Aus meiner Sicht ist das ein Mangel an Bewusstsein. Wenn Sie sich andere bekannte JVM-Sprachen (Scala, Clojure) ansehen, werden veränderbare Objekte nur selten im Code angezeigt. Daher werden sie in Szenarien verwendet, in denen Single-Threading nicht ausreicht.
Ich lerne derzeit Clojure und habe ein wenig Erfahrung in Scala (4 Jahre und mehr in Java) und Ihr Codierungsstil ändert sich aufgrund des Bewusstseins für den Status.
quelle
Ich denke , ein wichtiger Faktor ignoriert wurde: Java Beans verlassen sich stark auf einen bestimmten Stil Objekte mutiert, und (besonders wenn man bedenkt der Quelle) durchaus ein paar Leute scheinen zu nehmen , dass als (oder auch die ) kanonische Beispiel dafür , wie alle Java sollte geschrieben werden.
quelle
Jedes Java-Unternehmenssystem, an dem ich in meiner Karriere gearbeitet habe, verwendet entweder Hibernate oder die Java Persistence API (JPA). Hibernate und JPA schreiben im Wesentlichen vor, dass Ihr System veränderbare Objekte verwendet, da die gesamte Voraussetzung darin besteht, dass sie Änderungen an Ihren Datenobjekten erkennen und speichern. Für viele Projekte ist die Einfachheit der Entwicklung, die Hibernate mit sich bringt, überzeugender als die Vorteile unveränderlicher Objekte.
Offensichtlich gibt es veränderbare Objekte schon viel länger als den Ruhezustand, so dass der Ruhezustand wahrscheinlich nicht die ursprüngliche Ursache für die Beliebtheit veränderbarer Objekte ist. Vielleicht ließ die Popularität von veränderlichen Objekten Hibernate gedeihen.
Aber wenn heute viele Nachwuchsprogrammierer mit Hibernate oder einem anderen ORM auf Unternehmenssysteme zugreifen, werden sie vermutlich die Gewohnheit haben, veränderbare Objekte zu verwenden. Frameworks wie Hibernate könnten die Popularität von veränderlichen Objekten festigen.
quelle
Ein wichtiger Punkt, der noch nicht erwähnt wurde, ist, dass es möglich ist, die Identität des Objekts, das diesen Zustand einkapselt, unveränderlich zu machen , wenn der Zustand eines Objekts veränderbar ist .
Viele Programme sind darauf ausgelegt, reale Dinge zu modellieren, die von Natur aus veränderlich sind. Angenommen, eine Variable
AllTrucks
enthält um 00:51 Uhr einen Verweis auf Objekt # 451, das die Wurzel einer Datenstruktur ist, die angibt, welche Ladung in allen Lastwagen einer Flotte zu diesem Zeitpunkt (00:51 Uhr) enthalten ist, und eine VariableBobsTruck
kann verwendet werden, um einen Verweis auf Objekt # 24601 zu erhalten, der auf ein Objekt verweist, das angibt, welche Ladung sich zu diesem Zeitpunkt (00:51 Uhr) in Bobs LKW befindet. Um 00:52 Uhr werden einige Lastwagen (einschließlich Bobs) be- und entladen, und die Datenstrukturen werden aktualisiert, sodassAllTrucks
nun eine Referenz auf eine Datenstruktur enthalten ist, die angibt, dass sich die Ladung ab 00:52 Uhr in allen Lastwagen befindet.Was soll mit passieren
BobsTruck
?Wenn die 'Fracht'-Eigenschaft jedes LKW-Objekts unveränderlich ist, repräsentiert das Objekt # 24601 für immer den Zustand, den Bobs LKW um 00:51 Uhr hatte. Wenn
BobsTruck
ein direkter Verweis auf Objekt # 24601 vorhanden ist, wird der aktuelle Status von Bobs Truck nicht mehr angezeigt, es sei denn, derAllTrucks
zu aktualisierende Code wird ebenfalls aktualisiertBobsTruck
. Beachten Sie ferner,BobsTruck
dass der Code, der aktualisiert werden kann,AllTrucks
nur dann aktualisiert werden kann, wenn der Code explizit dafür programmiert wurde , es sei denn, er ist in einer Form eines veränderlichen Objekts gespeichert .Wenn man in der Lage sein möchte,
BobsTruck
den Zustand von Bobs LKW zu beobachten, während alle Objekte unveränderlichBobsTruck
bleiben , könnte man eine unveränderliche Funktion sein, die angesichts des Werts,AllTrucks
der zu einem bestimmten Zeitpunkt vorliegt oder hatte, den Zustand von Bobs LKW bei ergibt diese Zeit. Man könnte es sogar zwei unveränderliche Funktionen haben lassen, von denen eine wie oben ist und die andere einen Verweis auf einen Flottenstatus und einen neuen LKW-Status akzeptieren und einen Verweis auf einen neuen Flottenstatus zurückgeben würde passte zum alten, nur dass Bobs Truck den neuen Zustand haben würde.Leider kann es ziemlich nervig und umständlich werden, wenn man eine solche Funktion jedes Mal verwenden muss, wenn man auf den Zustand von Bobs Truck zugreifen möchte. Ein alternativer Ansatz wäre, zu sagen, dass Objekt # 24601 immer und für immer (solange jemand einen Verweis darauf hat) den aktuellen Status von Bobs Truck darstellt. Code, der wiederholt auf den aktuellen Status von Bobs Truck zugreifen möchte, müsste nicht jedes Mal eine zeitaufwendige Funktion ausführen - er könnte einfach eine Suchfunktion ausführen, um herauszufinden, dass Objekt # 24601 Bobs Truck ist, und dann einfach Greifen Sie jederzeit auf dieses Objekt zu, um den aktuellen Status von Bobs Truck zu sehen.
Beachten Sie, dass der funktionale Ansatz in einer Umgebung mit nur einem Thread oder in einer Umgebung mit mehreren Threads nicht ohne Vorteile ist, in der Threads zumeist nur Daten beobachten und nicht ändern. Ein beliebiger Beobachter-Thread, der die in enthaltene Objektreferenz kopiert
AllTrucks
Wenn Sie dann den von Ihnen dargestellten LKW-Status überprüfen, wird der Status aller LKWs ab dem Zeitpunkt angezeigt, an dem die Referenz erfasst wurde. Jedes Mal, wenn ein Beobachter-Thread neuere Daten sehen möchte, kann er die Referenz erneut abrufen. Wenn andererseits der gesamte Zustand der Flotte durch ein einziges unveränderliches Objekt dargestellt würde, würde die Möglichkeit ausgeschlossen, dass zwei Threads gleichzeitig verschiedene Lastwagen aktualisieren, da jeder Thread, wenn er seinen eigenen Geräten überlassen würde, ein neues Objekt mit dem Namen "Flottenzustand" erzeugen würde der neue Zustand seines Lastwagens und die alten Zustände von jedem anderen. Die Richtigkeit kann gewährleistet werden, wenn jeder Thread nur dannCompareExchange
zum Aktualisieren verwendet wird ,AllTrucks
wenn er sich nicht geändert hat, und auf einen Fehler reagiertCompareExchange
Durch die Neuerstellung des Statusobjekts und die Wiederholung der Operation. Wenn jedoch mehr als ein Thread versucht, gleichzeitig zu schreiben, ist die Leistung im Allgemeinen schlechter als wenn alle Schreibvorgänge in einem einzigen Thread ausgeführt würden. Je mehr Threads solche gleichzeitigen Operationen ausführen, desto schlechter wird die Leistung.Wenn einzelne LKW-Objekte veränderlich sind, aber unveränderliche Identitäten aufweisen , wird das Multithread-Szenario übersichtlicher. Es darf immer nur ein Thread gleichzeitig auf einem bestimmten LKW ausgeführt werden. Threads, die auf verschiedenen LKWs ausgeführt werden, können dies jedoch ohne Beeinträchtigung tun. Es gibt zwar Möglichkeiten, ein solches Verhalten zu emulieren, auch wenn unveränderliche Objekte verwendet werden (z. B. können die "AllTrucks" -Objekte so definiert werden, dass zum Festlegen des Zustands des zu XXX gehörenden Lastwagens für SSS lediglich ein Objekt mit der Angabe "Ab [Uhrzeit]" generiert werden muss. der Zustand des Lastwagens, der zu [XXX] gehört, ist jetzt [SSS], der Zustand von allem anderen ist [Alter Wert von AllTrucks]
CompareExchange
Schleife würde nicht lange dauern. Andererseits würde die Verwendung einer solchen Datenstruktur die zum Auffinden des Lastwagens einer bestimmten Person erforderliche Zeit erheblich erhöhen. Die Verwendung von veränderlichen Objekten mit unveränderlichen Identitäten vermeidet dieses Problem.quelle
Es gibt kein Richtig oder Falsch, es kommt nur darauf an, was Sie bevorzugen. Es gibt einen Grund, warum manche Menschen Sprachen bevorzugen, die ein Paradigma gegenüber einem anderen und ein Datenmodell gegenüber einem anderen bevorzugen. Es hängt nur von Ihren Vorlieben ab und davon, was Sie erreichen möchten (und es ist ein heiliger Gral, nach dem manche Sprachen suchen, wenn Sie beide Ansätze problemlos anwenden können, ohne eingefleischte Fans der einen oder anderen Seite zu entfremden).
Ich denke, die beste und schnellste Möglichkeit, Ihre Frage zu beantworten, besteht darin, dass Sie sich mit den Vor- und Nachteilen von Unveränderlichkeit und Veränderlichkeit befassen .
quelle
Überprüfen Sie diesen Blog-Beitrag: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . Es fasst zusammen, warum unveränderliche Objekte besser als veränderlich sind. Hier ist eine kurze Liste von Argumenten:
Soweit ich weiß, verwenden die Leute veränderbare Objekte, weil sie immer noch OOP mit imperativer prozeduraler Programmierung mischen.
quelle
In Java benötigen unveränderliche Objekte einen Konstruktor, der alle Eigenschaften des Objekts übernimmt (oder der Konstruktor erstellt sie aus anderen Argumenten oder Standardwerten). Diese Eigenschaften sollten als endgültig markiert werden .
Es gibt vier Probleme, die hauptsächlich mit der Datenbindung zusammenhängen :
Sie können # 1 und # 2 mit Anmerkungen wie
@ConstructorProperties
und durch Erstellen eines anderen veränderlichen Builder-Objekts (normalerweise fließend) entschärfen , um das unveränderliche Objekt zu erstellen.quelle
Ich bin überrascht, dass niemand die Vorteile der Leistungsoptimierung erwähnt hat. Je nach Sprache kann ein Compiler beim Umgang mit unveränderlichen Daten eine Reihe von Optimierungen vornehmen, da er weiß, dass sich die Daten niemals ändern werden. Alle möglichen Dinge werden übersprungen, was Ihnen enorme Leistungsvorteile bringt.
Auch unveränderliche Objekte eliminieren fast die gesamte Klasse der State Bugs.
Es ist nicht so groß, wie es sein sollte, weil es schwieriger ist, nicht für jede Sprache gilt und den meisten Menschen Imperative Codierung beigebracht wurde.
Außerdem habe ich festgestellt, dass die meisten Programmierer zufrieden sind und sich neuen Ideen widersetzen, die sie nicht vollständig verstehen. Im Allgemeinen mögen die Menschen keine Veränderung.
Denken Sie auch daran, dass der Zustand der meisten Programmierer schlecht ist. Die meisten Programme, die in freier Wildbahn erstellt werden, sind schrecklich und werden durch mangelndes Verständnis und mangelnde Politik verursacht.
quelle
Warum nutzen die Leute ein mächtiges Feature? Warum verwenden die Leute Meta-Programmierung, Faulheit oder dynamisches Tippen? Die Antwort ist Bequemlichkeit. Der veränderbare Zustand ist so einfach. Das Update ist so einfach, und der Schwellenwert für die Projektgröße, bei dem der unveränderliche Status produktiver ist als der veränderbare Status, ist ziemlich hoch, sodass Sie die Wahl für eine Weile nicht mehr loswerden.
quelle
Programmiersprachen sind für die Ausführung durch Computer ausgelegt. Alle wichtigen Bausteine von Computern - CPUs, RAM, Cache, Festplatten - sind veränderbar. Wenn sie nicht (BIOS) sind, sind sie wirklich unveränderlich und Sie können auch keine neuen unveränderlichen Objekte erstellen.
Daher weist jede Programmiersprache, die auf unveränderlichen Objekten basiert, eine Repräsentationslücke in ihrer Implementierung auf. Und für frühe Sprachen wie C war das ein großes Hindernis.
quelle
Ohne veränderbare Objekte gibt es keinen Staat. Zugegebenermaßen ist dies eine gute Sache, wenn Sie sie verwalten können und die Möglichkeit besteht, dass ein Objekt von mehr als einem Thread referenziert wird. Aber das Programm wird ziemlich langweilig. Bei vielen Programmen, insbesondere bei Webservern, wird die Verantwortung für veränderbare Objekte vermieden, indem die Veränderbarkeit von Datenbanken, Betriebssystemen, Systembibliotheken usw. abgeschoben wird. In der Praxis befreit dies den Programmierer von Veränderbarkeitsproblemen und macht das Web (und andere) Entwicklung erschwinglich. Aber die Veränderbarkeit ist immer noch da.
Im Allgemeinen gibt es drei Arten von Klassen: normale, nicht thread-sichere Klassen, die sorgfältig geschützt und geschützt werden müssen; unveränderliche Klassen, die frei verwendet werden können; und veränderbare, thread-sichere Klassen, die frei verwendet werden können, aber mit äußerster Sorgfalt geschrieben werden müssen. Der erste Typ ist der problematische, wobei die schlimmsten diejenigen sind, von denen angenommen wird , dass sie vom dritten Typ sind. Natürlich sind die ersten Typen einfach zu schreiben.
Normalerweise habe ich viele normale, wandelbare Klassen, die ich sehr genau beobachten muss. In einer Multithread-Situation verlangsamt die erforderliche Synchronisierung alles, auch wenn ich eine tödliche Umarmung vermeiden kann. Daher mache ich normalerweise unveränderliche Kopien der veränderlichen Klasse und übergebe diese an jeden, der sie verwenden kann. Jedes Mal, wenn das Original mutiert, wird eine neue unveränderliche Kopie benötigt, daher stelle ich mir vor, dass ich manchmal hundert Kopien des Originals da draußen habe. Ich bin absolut abhängig von Garbage Collection.
Zusammenfassend sind nicht thread-sichere, veränderbare Objekte in Ordnung, wenn Sie nicht mehrere Threads verwenden. (Multithreading ist jedoch überall unangenehm - seien Sie vorsichtig!) Sie können ansonsten sicher verwendet werden, wenn Sie sie auf lokale Variablen beschränken oder sie rigoros synchronisieren. Wenn Sie sie vermeiden können, indem Sie den bewährten Code anderer Personen (DBs, Systemaufrufe usw.) verwenden, tun Sie dies. Wenn Sie können eine unveränderliche Klasse verwenden, tun dies. Und ich denke , im Allgemeinen sind sich die Leute der Multithreading-Probleme entweder nicht bewusst oder haben (vernünftigerweise) Angst davor und wenden alle Arten von Tricks an, um Multithreading zu vermeiden (oder vielmehr die Verantwortung dafür woanders zu übernehmen).
Als PS spüre ich, dass Java-Getter und -Setter außer Kontrolle geraten. Überprüfen Sie dies aus.
quelle
Viele Leute hatten gute Antworten, daher möchte ich auf etwas hinweisen, das Sie angesprochen haben und das sehr aufmerksam und äußerst wahr ist und das an keiner anderen Stelle erwähnt wurde.
Das automatische Erstellen von Setzern und Gettern ist eine schreckliche, schreckliche Idee, aber es ist der erste Weg, wie prozedural denkende Menschen versuchen, OO in ihre Denkweise zu zwingen. Setter und Getter sowie Eigenschaften sollten nur erstellt werden, wenn Sie sie benötigen, und nicht standardmäßig
Obwohl Sie regelmäßig Getter benötigen, sollten Setter oder beschreibbare Eigenschaften in Ihrem Code nur über ein Builder-Muster vorhanden sein, bei dem sie gesperrt werden, nachdem das Objekt vollständig instanziiert wurde.
Viele Klassen sind nach der Erstellung veränderbar, was in Ordnung ist. Sie sollten ihre Eigenschaften nur nicht direkt manipulieren. Stattdessen sollten sie aufgefordert werden, ihre Eigenschaften durch Methodenaufrufe mit tatsächlicher Geschäftslogik zu manipulieren (Ja, ein Setter ist so ziemlich dasselbe) als direkt die Eigenschaft manipulieren)
Dies gilt nicht wirklich für Code / Sprachen im "Scripting" -Stil, sondern für Code, den Sie für eine andere Person erstellen und von der andere erwartet, dass er im Laufe der Jahre wiederholt gelesen wird. Ich musste in letzter Zeit anfangen, diese Unterscheidung zu treffen, weil ich es so sehr genieße, mit Groovy herumzuspielen, und es gibt einen großen Unterschied bei den Zielen.
quelle
Veränderbare Objekte werden verwendet, wenn Sie nach der Instanziierung des Objekts mehrere Werte festlegen müssen.
Sie sollten keinen Konstruktor mit beispielsweise sechs Parametern haben. Stattdessen ändern Sie das Objekt mit Setter-Methoden.
Ein Beispiel hierfür ist ein Report-Objekt mit Setzern für Schriftart, Ausrichtung usw.
Kurz gesagt: Mutables sind nützlich, wenn Sie einen hohen Status für ein Objekt festlegen müssen und es nicht praktisch wäre, eine sehr lange Konstruktorsignatur zu haben.
BEARBEITEN: Builder-Muster können verwendet werden, um den gesamten Status des Objekts zu erstellen.
quelle
new ReportBuilder().font("Arial").orientation("landscape").build()
withFont
zurückgibtReport
.Ich denke, die Verwendung von veränderlichen Objekten ergibt sich aus dem imperativen Denken: Sie berechnen ein Ergebnis, indem Sie den Inhalt von veränderlichen Variablen schrittweise ändern (Berechnung nach Nebeneffekt).
Wenn Sie funktional denken, möchten Sie einen unveränderlichen Zustand haben und nachfolgende Zustände eines Systems darstellen, indem Sie Funktionen anwenden und aus alten Werten neue Werte erstellen.
Der funktionale Ansatz kann sauberer und robuster sein, er kann jedoch aufgrund des Kopierens sehr ineffizient sein, sodass Sie auf eine gemeinsam genutzte Datenstruktur zurückgreifen möchten, die Sie schrittweise ändern.
Der für mich vernünftigste Kompromiss ist: Beginnen Sie mit unveränderlichen Objekten und wechseln Sie dann zu veränderlichen Objekten, wenn Ihre Implementierung nicht schnell genug ist. Unter diesem Gesichtspunkt kann die systematische Verwendung von veränderlichen Objekten von Anfang an als vorzeitige Optimierung angesehen werden : Sie wählen von Anfang an die effizientere (aber auch schwieriger zu verstehende und zu debuggende) Implementierung.
Warum verwenden viele Programmierer veränderbare Objekte? IMHO aus zwei Gründen:
quelle
Ich weiß, dass Sie nach Java fragen, aber in Objective-C verwende ich ständig veränderliche vs. unveränderliche. Es gibt ein unveränderliches Array NSArray und ein veränderliches Array NSMutableArray. Hierbei handelt es sich um zwei verschiedene Klassen, die speziell für die genaue Verwendung optimiert wurden. Wenn ich ein Array erstellen und dessen Inhalt niemals ändern muss, verwende ich NSArray, ein kleineres Objekt, das im Vergleich zu einem veränderlichen Array viel schneller ist.
Wenn Sie also ein unveränderliches Person-Objekt erstellen, benötigen Sie nur einen Konstruktor und Getter. Dadurch wird das Objekt kleiner und benötigt weniger Speicher, wodurch Ihr Programm tatsächlich schneller wird. Wenn Sie das Objekt nach der Erstellung ändern müssen, ist ein veränderbares Personenobjekt besser geeignet, damit die Werte geändert werden können, anstatt ein neues Objekt zu erstellen.
Also: Je nachdem, was Sie mit dem Objekt vorhaben, kann die Wahl von „wandelbar“ oder „unveränderlich“ einen großen Unterschied in Bezug auf die Leistung bedeuten.
quelle
Zusätzlich zu vielen anderen hier angeführten Gründen besteht ein Problem darin, dass die Hauptsprachen die Unveränderlichkeit nicht gut unterstützen. Zumindest werden Sie für die Unveränderlichkeit dadurch bestraft, dass Sie zusätzliche Schlüsselwörter wie const oder final hinzufügen und unleserliche Konstruktoren mit vielen, vielen Argumenten oder codelangen Buildermustern verwenden müssen.
Das ist viel einfacher in Sprachen, die auf Unveränderlichkeit ausgelegt sind. Betrachten Sie dieses Scala-Snippet zum Definieren einer Klasse Person, optional mit benannten Argumenten, und zum Erstellen einer Kopie mit einem geänderten Attribut:
Wenn Sie also möchten, dass Entwickler die Unveränderlichkeit übernehmen, ist dies einer der Gründe, warum Sie möglicherweise in Erwägung ziehen, auf eine modernere Programmiersprache zu wechseln.
quelle
Unveränderlich bedeutet, dass Sie den Wert nicht ändern können, und veränderlich bedeutet, dass Sie den Wert ändern können, wenn Sie in Primitiven und Objekten denken. Objekte unterscheiden sich von Grundelementen in Java darin, dass sie in Typen erstellt werden. Primitive werden in Typen wie int, boolean und void erstellt.
Viele Leute denken, dass Primitive und Objektvariablen, denen ein abschließender Modifikator vorangestellt ist, unveränderlich sind. Dies ist jedoch nicht genau der Fall. Final bedeutet also fast nicht unveränderlich für Variablen. Schauen Sie sich diesen Link für ein Codebeispiel an:
quelle
Ich denke, ein guter Grund wäre, "echte" veränderbare "Objekte" wie Interface-Fenster zu modellieren. Ich erinnere mich vage, dass ich gelesen hatte, dass das OOP erfunden wurde, als jemand versuchte, Software zur Steuerung eines Frachthafenbetriebs zu schreiben.
quelle
Java erfordert in vielen Fällen veränderbare Objekte, z. B. wenn Sie einen Besucher oder eine ausführbare Datei zählen oder zurückgeben möchten, benötigen Sie endgültige Variablen, auch ohne Multithreading.
quelle
final
ist eigentlich etwa ein Viertel eines Schrittes in Richtung Unveränderlichkeit. Nicht zu sehen, warum Sie es erwähnen.final
. Wenn man es in diesem Zusammenhang aufbringt, entsteht eine merkwürdige Verschmelzung vonfinal
"veränderlich", wennfinal
es darum geht , bestimmte Arten von Mutationen zu verhindern . (Übrigens nicht meine Ablehnung.)