Frage

Ich versuche, meinen Kopf um wandelbar vs unveränderliche Objekte zu erhalten. Mit veränderbaren Objekten wird (Rückkehr zum Beispiel ein Array von Strings von einem Verfahren) eine Menge schlechter Presse, aber ich habe Probleme zu verstehen, was die negativen Auswirkungen dieser sind. Was sind die Best Practices rund um veränderbare Objekte verwenden? Sollten Sie sie, wann immer möglich vermeiden?

War es hilfreich?

Lösung

Nun, es gibt ein paar Aspekte. Die Nummer eins, veränderbare Objekte ohne Referenz-Identität kann Fehler zu ungewöhnlichen Zeiten führen. Betrachten wir zum Beispiel eine Person Bohne mit einem wertbasierten equals Methode:

Map<Person, String> map = ...
Person p = new Person();
map.put(p, "Hey, there!");

p.setName("Daniel");
map.get(p);       // => null

Die Person Instanz wird in der Karte „verloren“, wenn als Schlüssel verwendet, weil es hashCode und Gleichheit beruhten auf veränderbare Werte. Diese Werte außerhalb der Karte geändert und all die Hashing wurde obsolet. Theoretikern wie in diesem Punkt zu lamentieren, aber in der Praxis habe ich gefunden, es nicht zu viel von einem Problem zu sein.

Ein weiterer Aspekt ist die logische „Vernünftigkeit“ des Codes. Das ist ein harter Begriff zu definieren, alles von Lesbarkeit umfasst zu fließen. Generisch sollten Sie in der Lage sein, in einem Stück Code zu sehen und leicht zu verstehen, was es tut. Aber wichtiger als das, sollten Sie in der Lage sein, sich selbst zu überzeugen, dass es das tut, was es tut richtig . Wenn Objekte „Domänen“ unabhängig über verschiedene Code ändern können, wird es manchmal schwierig, den Überblick zu behalten, was wo und warum ( „spooky Fernwirkung“). Dies ist ein schwieriges Konzept zu veranschaulichen, aber es ist etwas, das oft in größeren, komplexeren Architekturen konfrontiert ist.

Schließlich veränderbare Objekte sind Killer in gleichzeitigen Situationen. Jedes Mal, wenn Sie ein veränderbares Objekt aus separaten Threads zugreifen, müssen Sie mit Verriegelung beschäftigen. Dies reduziert den Durchsatz und macht den Code dramatisch schwieriger aufrecht zu erhalten. Ein ausreichend kompliziertes System bläst dieses Problem bisher in keinem Verhältnis, dass es fast unmöglich wird, zu halten (auch für Concurrency-Experten).

Unveränderliche Objekte (und insbesondere unveränderlich Sammlungen) all diese Probleme vermeiden. Sobald Sie Ihren Verstand umgehen, wie sie funktionieren, wird Ihr Code in etwas zu entwickeln, die einfacher zu lesen ist, leichter zu pflegen und weniger wahrscheinlich, dass in ungerade und unvorhersehbarer Weise zum Scheitern verurteilt. Unveränderliche Objekte sind noch einfacher zu testen, nicht nur auf ihre leicht mockability, sondern auch die Codemuster neigen sie dazu, zu erzwingen. Kurz gesagt, sie sind alle um gute Praxis!

Mit diesem wird gesagt, ich bin kaum ein Eiferer in dieser Angelegenheit. Einige Probleme modellieren einfach nicht schön, wenn alles unveränderlich ist. Aber ich glaube, dass Sie versuchen sollen, so viel von Ihrem Code in dieser Richtung wie möglich zu drücken, vorausgesetzt natürlich, dass Sie eine Sprache verwenden, das diese vertretbare Meinung macht (C / C ++ macht dies sehr schwierig, genau wie Java) . Kurz gesagt: hängen die Vorteile etwas zu Ihrem Problem, aber ich würde dazu neigen, Unveränderlichkeit zu bevorzugen

.

Andere Tipps

unveränderliche Objekte vs. Immutable Sammlungen

Eine der feineren Punkte in der Debatte über wandelbar vs. unveränderliche Objekte ist die Möglichkeit, das Konzept der Unveränderlichkeit zu einer Sammlung zu erweitern. Ein unveränderliches Objekt ist ein Objekt, das eine einzelne logische Struktur der Daten (beispielsweise ein unveränderliches string) oft darstellt. Wenn Sie einen Verweis auf ein unveränderliches Objekt haben, wird nicht der Inhalt des Objekts ändern.

Eine unveränderliche Sammlung ist eine Sammlung, die sich nie ändert.

Wenn ich eine Operation für eine veränderbare Sammlung durchführen, dann ändere ich die Sammlung an Ort und Stelle, und alle Unternehmen, die Verweise auf die Sammlung sehen, die Änderung hat.

Wenn ich eine Operation auf einer unveränderlichen Sammlung durchzuführen, wird ein Verweis auf eine neue Kollektion spiegelt die Änderung zurück. Alle Elemente, die Verweise auf frühere Versionen der Sammlung haben wird die Änderung nicht sehen.

Clever-Implementierungen müssen nicht unbedingt kopieren (Klon), um die gesamte Kollektion, um diese Unveränderlichkeit zu liefern. Das einfachste Beispiel ist der Stapel als einfach verkettete Liste implementiert und die Push / Pop-Operationen. Sie können alle Knoten aus der vorherige Kollektion in der neuen Kollektion wieder verwenden, nur einen einzigen Knoten für die Push-Zugabe und das Klonen keine Knoten für den Pop. Der push_tail Betrieb auf einer einfach verketteten Liste, auf der anderen Seite, ist nicht so einfach oder effizient.

Immutable vs. Mutable Variablen / Referenzen

Einige funktionalen Sprachen nehmen das Konzept der Unveränderlichkeit Referenzen selbst zu widersprechen, so dass nur eine einzige Referenzzuordnung.

  • In Erlang dies für alle „Variablen“ wahr ist. Ich kann nur noch Objekte mit einer Referenz zuweisen. Wenn ich auf einer Sammlung arbeiten, würde ich nicht in der Lage sein, die neue Kollektion der alten Referenz (Variablenname) neu zuzuweisen.
  • Scala baut auch diese in die Sprache mit allen Referenzen mit erklären wird var oder val , vals nur einzelne Zuordnung ist und einen funktionalen Stil zu fördern, aber vars ermöglicht eine c artige oder Java-ähnliche Programmstruktur.
  • Die var / val Anmeldung erforderlich ist, während viele traditionellen Sprachen optionale Modifikatoren verwenden wie letzte in Java und const in c.

Einfache Entwicklung vs. Leistung

Fast immer der Grund, ein unveränderliches Objekt zu verwenden, ist frei von Nebenwirkungen Programmierung und einfache Argumentation über den Code (insbesondere in einer stark konkurrierenden / Parallel-Umgebung) zu fördern. Sie müssen sich keine Sorgen über die zugrunde liegenden Daten von einem anderen Unternehmen geändert werden, wenn das Objekt unveränderlich ist.

Der Hauptnachteil ist die Leistung. Hier ist eine Zuschreibung auf einen einfachen Test, den ich in Java tat einig unveränderlichen vs. veränderbare Objekte in einem Spielzeug-Problem zu vergleichen .

Die Performance-Probleme werden in vielen Anwendungen moot, aber nicht alle, weshalb viele große numerische Pakete, wie die Numpy Array-Klasse in Python, für Updates von großen Arrays In-Place ermöglichen. Dies würde für die Anwendungsbereiche wichtig sein, die Verwendung von großen Matrix- und Vektoroperationen machen. Diese großen Daten parallel und rechenintensive Probleme erreichen eine große Geschwindigkeit-up anstelle betrieben wird.

Überprüfen Sie dieses Blog-Post: http://www.yegor256.com/ 2014/06/09 / Objekte-sollte-sein-immutable.html . Es erklärt, warum unveränderliche Objekte sind besser als wandelbar. Kurz gesagt:

  • unveränderliche Objekte sind einfacher zu konstruieren, testen und nutzen
  • wirklich unveränderliche Objekte sind immer Thread-sicher
  • Sie helfen zeitliche Kopplung zu vermeiden
  • ihre Nutzung ist nebenwirkungsfreie (keine defensiven Kopien)
  • Identität Veränderlichkeit Problem vermieden
  • Sie haben Fehleratomizität immer
  • Sie sind viel leichter cachen

Unveränderliche Objekte sind ein sehr leistungsfähiges Konzept. Sie nehmen viel von der Last der versucht, weg Objekte / Variablen konsistent für alle Kunden zu halten.

Sie können sie für niedriges Niveau, nicht-polymorphe Objekte verwenden - wie ein CPoint Klasse -., Die meist mit Wertesemantik verwendet werden

Sie können sie für hohe, polymorphe Schnittstellen verwenden - wie ein iFunction eine mathematische Funktion darstellen, -., Die sich ausschließlich mit dem Objekt Semantik verwendet wird

Größter Vorteil: Unveränderlichkeit + Objekt Semantik + intelligente Zeiger machen Objektbesitz kein Thema, alle Kunden der das Objekt standardmäßig ihre eigene private Kopie haben. Implizit bedeutet dies auch, deterministisches Verhalten in Gegenwart von Gleichzeitigkeit.

Nachteil: wenn sie mit Objekten viele Daten verwendet, den Speicherverbrauch kann ein Problem werden. Eine Lösung dieses Problems könnte sein, Operationen symbolisch für ein Objekt zu halten, und faul Auswertung zu tun. Dies kann jedoch dann zu Ketten symbolischer Berechnungen führen, die sich negativ auf die Leistung auswirken können, wenn die Schnittstelle nicht ausgelegt ist, symbolische Operationen zu empfangen. Etwas auf jeden Fall in diesem Fall zu vermeiden zurückkehrt große Teile des Speichers von einem Verfahren. In Kombination mit verketteten symbolischen Operationen zu massivem Speicherverbrauch und Leistungseinbußen führen könnte.

So unveränderliche Objekte sind auf jeden Fall meine primäre Art und Weise über objektorientiertes Design des Denkens, aber sie sind kein Dogma. Sie lösen eine Menge Probleme für die Kunden von Objekten, sondern auch viele schaffen, vor allem für die Implementierer.

Sie sollten angeben, welche Sprache Sie sprechen. Für Low-Level-Sprachen wie C oder C ++, ziehe ich veränderbare Objekte zu verwenden, um Platz zu sparen und Speicher Abwanderungs zu reduzieren. In höheren Sprachen, machen unveränderliche Objekte leichter über das Verhalten des Codes zur Vernunft (vor allem Multi-Threaded-Code), weil es keine „in einem Abstand spooky action“ ist.

Ein veränderbares Objekt ist einfach ein Objekt, das geändert werden kann, nachdem es erstellt wird / instanziiert, gegen ein unveränderliches Objekt, das nicht geändert werden kann (siehe Wikipedia zum Thema). Ein Beispiel hierfür in einer Programmiersprache ist Pythons Listen und Tupel. Listen können modifiziert werden (beispielsweise können neue Elemente hinzugefügt werden, nachdem es erstellt wird), während Tupel nicht.

Ich glaube nicht wirklich, es gibt eine clearcut Antwort auf die Frage, die man besser für alle Situationen ist. Sie haben beide ihre Plätze.

Wenn ein Klassentyp ist wandelbar, eine Variable dieser Klasse-Typ kann eine Reihe verschiedener Bedeutungen haben. Angenommen, ein Objekt foo ein Feld int[] arr hat, und sie hält einen Verweis auf eine int[3] die Zahlen mit {5, 7, 9}. Auch wenn die Art des Feldes bekannt ist, gibt es mindestens vier verschiedene Dinge, die sie darstellen können:

  • Ein möglicherweise gemeinsam genutzten Referenz, alle Inhaber von deren Pflege nur, dass sie die Werte 5, 7 kapselt und 9. Wenn foo arr will verschiedene Werte verkapseln, muss sie es mit einem anderen Array ersetzen, die enthält gewünschte Werte ein. Wenn man eine Kopie foo machen will, kann man der Kopie entweder einen Verweis geben arr oder ein neues Array die Werte mit {1,2,3}, je nachdem, was bequemer ist.

  • Der einzige Hinweis, überall in der Welt, zu einem Array, das die Werte 5, 7 und 9. Satz von drei Speicherplätze, die die Werte 5 halten, in dem Moment kapselt, 7 und 9; wenn foo will die Werte 5, 8 und 9 verkapseln, kann es entweder das zweite Element in dem Array ändern oder ein neues Array erstellen, um die Werte, die 5, 8 und 9 und die alten verlassen. Beachten Sie, dass, wenn man eine Kopie foo, muss man in der Kopie ersetzen arr mit einem Verweis auf ein neues Array machen, um wollte foo.arr zu diesem Array als einzige Referenz bleiben überall im Universum.

  • Ein Verweis auf ein Array, das von einigen gehört andere Objekt, das es aus irgendeinem Grund foo ausgesetzt (z vielleicht will es foo einige Daten speichern dort). In diesem Szenario wird arr den Inhalt des Arrays nicht einkapseln, sondern seine Identität . arr mit einem Verweis auf ein neues Array, weil ersetzt völlig seine Bedeutung ändern würde, sollte eine Kopie von foo einen Verweis auf das gleiche Array halten.

  • Eine Referenz auf ein Array von denen foo ist der alleinige Eigentümer, sondern an den Referenzen von anderem Objekt aus irgendeinem Grunde gehalten werden (zB sie will es das andere Objekt zum Speichern von Daten haben - die Kehrseite der vorheriger Fall). In diesem Szenario kapselt arr sowohl die Identität des Feldes und seinen Inhalt. Ersetzen arr mit einem Verweis auf ein neues Array wäre völlig seine Bedeutung verändern, sondern ein Klon des arr würde die Annahme foo.arr verweisen, die verletzen, dass foo alleiniger Eigentümer ist. Es gibt also keine Möglichkeit, foo zu kopieren.

In der Theorie sollte int[] eine schöne einfach gut definierte Art, aber es hat vier sehr unterschiedliche Bedeutungen. Im Gegensatz dazu ein Verweis auf ein unveränderliches Objekt (z.B. String) weist im allgemeinen nur eine Bedeutung. Ein großer Teil der „Macht“ der unveränderliche Objekte ergibt sich aus dieser Tatsache.

Wenn Sie Referenzen eines Array oder String zurück, dann außen um den Inhalt in diesem Objekt ändern kann, und daher macht es so wandelbar (änderbar) Objekt.

Immutable Mittel können nicht geändert werden, und wandelbar bedeutet, dass Sie ändern können.

Objekte sind anders als Primitive in Java. Primitives werden in Typen gebaut (boolean, int, usw.) und Objekten (Klassen) sind vom Benutzer erstellt Typen.

Primitives und Objekte können wandelbar oder unveränderlich sein, wenn sie als Elementvariablen innerhalb der Implementierung einer Klasse definiert sind.

Eine Menge Leute Leute Primitiven denken und Objektvariablen eine endgültige Modifier infront von ihnen haben, sind unveränderlich, aber das ist nicht ganz richtig. So final bedeutet fast nicht unveränderlich für Variablen. Siehe Beispiel hier
http://www.siteconsortium.com/h/D0000F.php .

Mutable Instanzen wird als Referenz übergeben.

Immutable Instanzen wird als Wert übergeben.

Abstrakte Beispiel. Nehmen wir an, die es gibt eine Datei mit dem Namen txtfile in meiner Festplatte. Wenn Sie nun fragen, txtfile von mir, ich habe es in zwei Modi zurückkehren kann:

  1. Erstellen Sie eine Verknüpfung zu txtfile und pas Verknüpfung zu Ihnen, oder
  2. Nehmen Sie eine Kopie für txtfile und pas kopieren Sie.

Im ersten Modus zurück txtfile ist ein veränderliches Datei, weil, wenn Sie Änderungen in Verknüpfungsdatei tun, was Sie tun Änderungen in Original zu Datei. Vorteil dieses Modus ist, dass jeder zurück Verknüpfung erforderlich weniger Speicher (auf RAM oder HDD) und Nachteil ist, dass jeder (nicht nur ich, Eigentümer) haben Berechtigungen Dateiinhalt zu ändern.

Im zweiten Modus zurück txtfile ist eine unveränderliche Datei, da alle Änderungen in der empfangenen Datei nicht auf die Originaldatei beziehen. Vorteil dieses Verfahrens ist, dass nur ich (Inhaber) Originaldatei und Nachteil ändern kann, ist, dass jeder zurück Kopie benötigter Speicher (RAM oder HDD).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top