Ich versuche, mich mit veränderlichen und unveränderlichen Objekten zu beschäftigen. Die Verwendung von veränderlichen Objekten führt zu viel Druck (z. B. Rückgabe einer Reihe von Zeichenfolgen aus einer Methode), aber ich habe Probleme zu verstehen, welche negativen Auswirkungen dies hat. Was sind die Best Practices für die Verwendung veränderlicher Objekte? Sollten Sie sie nach Möglichkeit vermeiden?
oop
immutability
mutable
Alex Angas
quelle
quelle
string
ist unveränderlich, zumindest in .NET, und ich denke auch in vielen anderen modernen Sprachen.Antworten:
Nun, das hat einige Aspekte.
Veränderbare Objekte ohne Referenzidentität können zu ungeraden Zeiten Fehler verursachen. Betrachten Sie beispielsweise eine
Person
Bean mit einer wertbasiertenequals
Methode:Die
Person
Instanz geht in der Karte "verloren", wenn sie als Schlüssel verwendet wird, da ihrehashCode
und ihre Gleichheit auf veränderlichen Werten basieren. Diese Werte haben sich außerhalb der Karte geändert und das gesamte Hashing ist veraltet. Theoretiker mögen es, in diesem Punkt zu harfen, aber in der Praxis habe ich es nicht als zu großes Problem empfunden.Ein weiterer Aspekt ist die logische "Vernünftigkeit" Ihres Codes. Dies ist ein schwer zu definierender Begriff, der alles von der Lesbarkeit bis zum Ablauf umfasst. Generell sollten Sie in der Lage sein, einen Code zu betrachten und leicht zu verstehen, was er tut. Wichtiger ist jedoch, dass Sie sich davon überzeugen können, dass es das tut, was es richtig macht . Wenn sich Objekte unabhängig voneinander über verschiedene Code- "Domänen" hinweg ändern können, wird es manchmal schwierig, den Überblick darüber zu behalten, was wo und warum ist (" gruselige Fernwirkung "). Es ist schwieriger, dieses Konzept zu veranschaulichen, aber es ist etwas, mit dem größere, komplexere Architekturen häufig konfrontiert sind.
Schließlich veränderbare Objekte sind Killer in konkurrierenden Situationen. Immer wenn Sie von separaten Threads aus auf ein veränderliches Objekt zugreifen, müssen Sie sich mit dem Sperren befassen. Dies reduziert den Durchsatz und erschwert die Wartung Ihres Codes erheblich . Ein ausreichend kompliziertes System lässt dieses Problem so weit überproportional werden, dass es (selbst für Parallelitätsexperten) nahezu unmöglich zu warten ist.
Unveränderliche Objekte (und insbesondere unveränderliche Sammlungen) vermeiden all diese Probleme. Sobald Sie sich ein Bild davon gemacht haben, wie sie funktionieren, entwickelt sich Ihr Code zu etwas, das leichter zu lesen, leichter zu warten und weniger wahrscheinlich auf seltsame und unvorhersehbare Weise ausfällt. Unveränderliche Objekte sind noch einfacher zu testen, nicht nur aufgrund ihrer einfachen Verspottbarkeit, sondern auch aufgrund der Codemuster, die sie tendenziell erzwingen. Kurz gesagt, sie sind überall eine gute Übung!
Trotzdem bin ich in dieser Angelegenheit kaum ein Eiferer. Einige Probleme lassen sich einfach nicht gut modellieren, wenn alles unveränderlich ist. Ich denke jedoch, dass Sie versuchen sollten, so viel Code wie möglich in diese Richtung zu verschieben, vorausgesetzt natürlich, Sie verwenden eine Sprache, die dies zu einer haltbaren Meinung macht (C / C ++ macht dies sehr schwierig, ebenso wie Java). . Kurz gesagt: Die Vorteile hängen etwas von Ihrem Problem ab, aber ich würde die Unveränderlichkeit eher bevorzugen.
quelle
Unveränderliche Objekte vs. unveränderliche Sammlungen
Einer der Feinheiten in der Debatte über veränderbare und unveränderliche Objekte ist die Möglichkeit, das Konzept der Unveränderlichkeit auf Sammlungen auszudehnen. Ein unveränderliches Objekt ist ein Objekt, das häufig eine einzelne logische Datenstruktur darstellt (z. B. eine unveränderliche Zeichenfolge). Wenn Sie einen Verweis auf ein unveränderliches Objekt haben, ändert sich der Inhalt des Objekts nicht.
Eine unveränderliche Sammlung ist eine Sammlung, die sich nie ändert.
Wenn ich eine Operation für eine veränderbare Sammlung ausführe, ändere ich die Sammlung an Ort und Stelle, und alle Entitäten, die Verweise auf die Sammlung haben, sehen die Änderung.
Wenn ich eine Operation für eine unveränderliche Sammlung ausführe, wird eine Referenz auf eine neue Sammlung zurückgegeben, die die Änderung widerspiegelt. Alle Entitäten, die Verweise auf frühere Versionen der Sammlung haben, sehen die Änderung nicht.
Clevere Implementierungen müssen nicht unbedingt die gesamte Sammlung kopieren (klonen), um diese Unveränderlichkeit zu gewährleisten. Das einfachste Beispiel ist der als einfach verknüpfte Liste implementierte Stapel und die Push / Pop-Operationen. Sie können alle Knoten aus der vorherigen Sammlung in der neuen Sammlung wiederverwenden, indem Sie nur einen einzigen Knoten für den Push hinzufügen und keine Knoten für den Pop klonen. Die push_tail-Operation für eine einfach verknüpfte Liste ist dagegen nicht so einfach oder effizient.
Unveränderliche vs. veränderbare Variablen / Referenzen
Einige funktionale Sprachen verwenden das Konzept der Unveränderlichkeit für Objektreferenzen selbst und erlauben nur eine einzige Referenzzuweisung.
Einfache Entwicklung vs. Leistung
Fast immer besteht der Grund für die Verwendung eines unveränderlichen Objekts darin, die nebenwirkungsfreie Programmierung und das einfache Denken über den Code zu fördern (insbesondere in einer Umgebung mit hoher Parallelität / Parallelität). Sie müssen sich keine Sorgen machen, dass die zugrunde liegenden Daten von einer anderen Entität geändert werden, wenn das Objekt unveränderlich ist.
Der Hauptnachteil ist die Leistung. Hier ist eine Zusammenfassung eines einfachen Tests, den ich in Java durchgeführt habe , um einige unveränderliche mit veränderlichen Objekten in einem Spielzeugproblem zu vergleichen.
Die Leistungsprobleme sind in vielen Anwendungen umstritten, aber nicht in allen, weshalb viele große numerische Pakete, wie die Numpy Array-Klasse in Python, direkte Aktualisierungen großer Arrays ermöglichen. Dies wäre wichtig für Anwendungsbereiche, die große Matrix- und Vektoroperationen verwenden. Diese großen datenparallelen und rechenintensiven Probleme erreichen eine große Beschleunigung, wenn sie an Ort und Stelle arbeiten.
quelle
Überprüfen Sie diesen Blog-Beitrag: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . Es erklärt, warum unveränderliche Objekte besser als veränderlich sind. Zusamenfassend:
quelle
Unveränderliche Objekte sind ein sehr mächtiges Konzept. Sie entlasten den Versuch, Objekte / Variablen für alle Clients konsistent zu halten, erheblich.
Sie können sie für nicht polymorphe Objekte auf niedriger Ebene verwenden - wie eine CPoint-Klasse -, die hauptsächlich mit Wertesemantik verwendet werden.
Oder Sie können sie für polymorphe Schnittstellen auf hoher Ebene verwenden - wie eine IF-Funktion, die eine mathematische Funktion darstellt -, die ausschließlich für die Objektsemantik verwendet wird.
Größter Vorteil: Unveränderlichkeit + Objektsemantik + intelligente Zeiger machen den Besitz von Objekten zu einem Problem. Alle Clients des Objekts verfügen standardmäßig über eine eigene private Kopie. Implizit bedeutet dies auch deterministisches Verhalten bei gleichzeitiger Anwendung.
Nachteil: Bei Verwendung mit Objekten, die viele Daten enthalten, kann der Speicherverbrauch zu einem Problem werden. Eine Lösung hierfür könnte darin bestehen, Operationen an einem Objekt symbolisch zu halten und eine verzögerte Auswertung durchzuführen. Dies kann dann jedoch zu Ketten symbolischer Berechnungen führen, die die Leistung negativ beeinflussen können, wenn die Schnittstelle nicht für symbolische Operationen ausgelegt ist. In diesem Fall sollten Sie unbedingt vermeiden, große Speicherblöcke von einer Methode zurückzugeben. In Kombination mit verketteten symbolischen Operationen kann dies zu einem massiven Speicherverbrauch und Leistungseinbußen führen.
Unveränderliche Objekte sind definitiv meine primäre Denkweise über objektorientiertes Design, aber sie sind kein Dogma. Sie lösen viele Probleme für Clients von Objekten, schaffen aber auch viele, insbesondere für die Implementierer.
quelle
Sie sollten angeben, über welche Sprache Sie sprechen. Für einfache Sprachen wie C oder C ++ bevorzuge ich die Verwendung veränderbarer Objekte, um Platz zu sparen und die Speicherabwanderung zu verringern. In höheren Sprachen erleichtern unveränderliche Objekte das Nachdenken über das Verhalten des Codes (insbesondere Multithread-Code), da es keine "gruselige Fernwirkung" gibt.
quelle
Ein veränderliches Objekt ist einfach ein Objekt, das geändert werden kann, nachdem es erstellt / instanziiert wurde, im Vergleich zu einem unveränderlichen Objekt, das nicht geändert werden kann (siehe die Wikipedia-Seite zum Thema). Ein Beispiel hierfür in einer Programmiersprache sind Pythons-Listen und -Tupel. Listen können geändert werden (z. B. können nach dem Erstellen neue Elemente hinzugefügt werden), Tupel hingegen nicht.
Ich glaube nicht, dass es eine eindeutige Antwort gibt, welche für alle Situationen besser ist. Sie haben beide ihre Plätze.
quelle
Wenn ein Klassentyp veränderbar ist, kann eine Variable dieses Klassentyps verschiedene Bedeutungen haben. Angenommen, ein Objekt
foo
hat ein Feldint[] arr
und enthält einen Verweis auf ein Objektint[3]
mit den Zahlen {5, 7, 9}. Obwohl die Art des Feldes bekannt ist, gibt es mindestens vier verschiedene Dinge, die es darstellen kann:Eine potentiell-shared Referenz, deren sämtliche Halter kümmert sich nur , dass sie die Werte 5, 7 und 9. Wenn kapselt
foo
Bedürfnissearr
unterschiedliche Werte verkapseln, muss sie durch eine andere Anordnung ersetzt werden , die die gewünschten Werte enthält. Wenn man eine Kopie von erstellen möchtefoo
, kann man der Kopie entweder einen Verweis aufarr
oder ein neues Array mit den Werten {1,2,3} geben, je nachdem, was bequemer ist.Der einzige Verweis irgendwo im Universum auf ein Array, das die Werte 5, 7 und 9 kapselt. Satz von drei Speicherorten, die im Moment die Werte 5, 7 und 9 enthalten. Wenn
foo
es die Werte 5, 8 und 9 kapseln soll, kann es entweder das zweite Element in diesem Array ändern oder ein neues Array mit den Werten 5, 8 und 9 erstellen und das alte verlassen. Beachten Sie, dass, wenn Sie eine Kopie vonfoo
erstellen möchten, diese in der Kopie durcharr
einen Verweis auf ein neues Array ersetzt werden mussfoo.arr
, um als einziger Verweis auf dieses Array irgendwo im Universum zu bleiben.Ein Verweis auf ein Array, das einem anderen Objekt gehört, das es
foo
aus irgendeinem Grund ausgesetzt hat (z. B. möchte es möglicherweisefoo
einige Daten dort speichern). In diesem Szenario wirdarr
der Inhalt des Arrays nicht gekapselt, sondern dessen Identität . Da das Ersetzenarr
durch einen Verweis auf ein neues Array seine Bedeutung vollständig ändern würde, sollte eine Kopie vonfoo
einen Verweis auf dasselbe Array enthalten.Ein Verweis auf ein Array,
foo
dessen alleiniger Eigentümer es ist, dessen Verweise jedoch aus irgendeinem Grund von einem anderen Objekt gehalten werden (z. B. möchte das andere Objekt Daten dort speichern - die Kehrseite des vorherigen Falls). In diesem Szenario werdenarr
sowohl die Identität des Arrays als auch sein Inhalt gekapselt. Das Ersetzenarr
durch einen Verweis auf ein neues Array würde seine Bedeutung völlig ändern, aber dasarr
Verweisen auf einen Klonfoo.arr
würde die Annahme verletzen, dassfoo
der alleinige Eigentümer ist. Es gibt also keine Möglichkeit zum Kopierenfoo
.Theoretisch
int[]
sollte es ein schöner einfacher, gut definierter Typ sein, aber er hat vier sehr unterschiedliche Bedeutungen. Im Gegensatz dazu hat eine Bezugnahme auf ein unveränderliches Objekt (z. B.String
) im Allgemeinen nur eine Bedeutung. Ein Großteil der "Kraft" unveränderlicher Objekte beruht auf dieser Tatsache.quelle
Veränderbare Instanzen werden als Referenz übergeben.
Unveränderliche Instanzen werden als Wert übergeben.
Abstraktes Beispiel. Angenommen, auf meiner Festplatte befindet sich eine Datei mit dem Namen txtfile . Wenn Sie mich jetzt um txtfile bitten , kann ich es in zwei Modi zurückgeben:
Im ersten Modus ist die zurückgegebene txt- Datei eine veränderbare Datei, da Sie bei Änderungen an der Verknüpfungsdatei auch Änderungen an der Originaldatei vornehmen. Der Vorteil dieses Modus besteht darin, dass für jede zurückgegebene Verknüpfung weniger Speicher (im RAM oder auf der Festplatte) erforderlich ist. Der Nachteil besteht darin, dass jeder (nicht nur ich, der Eigentümer) die Berechtigung zum Ändern des Dateiinhalts hat.
Im zweiten Modus ist die zurückgegebene txt- Datei eine unveränderliche Datei, da sich alle Änderungen in der empfangenen Datei nicht auf die Originaldatei beziehen. Der Vorteil dieses Modus ist, dass nur ich (der Besitzer) die Originaldatei ändern kann. Der Nachteil ist, dass für jede zurückgegebene Kopie Speicher erforderlich ist (im RAM oder auf der Festplatte).
quelle
Wenn Sie Referenzen eines Arrays oder einer Zeichenfolge zurückgeben, kann die Außenwelt den Inhalt in diesem Objekt ändern und ihn somit als veränderbares (veränderbares) Objekt festlegen.
quelle
Unveränderliche Mittel können nicht geändert werden, und veränderliche Mittel können geändert werden.
Objekte unterscheiden sich von Grundelementen in Java. Grundelemente sind in Typen (Boolean, Int usw.) erstellt, und Objekte (Klassen) sind vom Benutzer erstellte Typen.
Grundelemente und Objekte können veränderlich oder unveränderlich sein, wenn sie als Elementvariablen innerhalb der Implementierung einer Klasse definiert werden.
Viele Leute denken, dass Primitive und Objektvariablen mit einem letzten Modifikator unveränderlich sind, dies ist jedoch nicht genau richtig. Final bedeutet also fast nicht unveränderlich für Variablen. Siehe Beispiel hier
http://www.siteconsortium.com/h/D0000F.php .
quelle