Als Łukasz und Venkat Reddy Sudheer Aedama in ihren Kommentaren erwähnt, ist die empfohlene Methode, Werte in Scala zu mutieren ist unveränderliche Objekte zu verwenden.
Dies bedeutet, dass bei jeder Änderung des Objektstatus ein neues Objekt mit aktualisierten Werten erstellt wird. Es ist notwendig, eine referenzielle Transparenz zu erreichen, die es Programmen erlaubt, frei von Nebenwirkungen zu sein, was als eine wünschenswerte Eigenschaft angesehen wird, die den Code wesentlich einfacher zu pflegen macht.
In Scala gibt es einen bequemen Mechanismus namens Fallklassen. Schauen wir uns an, wie Door
als Fall Klasse definiert werden könnten:
case class Door(name: String, open: Boolean) {
def opened(open: Boolean) = copy(open = open)
}
Wie Sie es bemerkt haben keine var
oder val
vor der Klasse Argumente. Dies liegt daran, dass in der Fallklasse jedes Argument als val
angenommen wird, wodurch Fallklassen standardmäßig unveränderlich gemacht werden.
Ein weiterer Unterschied ist, dass statt einer neuen Instanz mit new
Operator eine copy
Methode verwendet wird, die es ermöglicht, die Werte der angegebenen Felder zu ändern. Es mag schwierig sein, den Vorteil zu sehen, dies jetzt so zu tun, aber es wird sehr praktisch, wenn Sie Fallklassen mit viel mehr Feldern behandeln.
Jetzt können Sie Door
instanziiert und es auf folgende Weise aktualisieren:
val door = Door("MyDoor", false)
door.name // "MyDoor"
door.open // false
val door2 = door.opened(true)
door2.name // "MyDoor"
door2.open // true
Eine weitere Bequemlichkeit von Fallklassen zur Verfügung gestellt ist, dass man sie ohne new
Schlüsselwort instanziieren, die für den Aufruf Door.apply("MyDoor", false)
ein syntaktischer Zucker ist.
Bis jetzt haben wir gesehen, wie eine Fallklasse für unveränderliche Datenobjekte zu deklarieren und zu verwenden. Aber heißt das, dass wir unsere Klassen immer so deklarieren sollten?
Die wichtige Eigenschaft von Fallklassen ist, dass sie transparent sind. Was durch die Tatsache auferlegt wird, dass alle ihre Felder als öffentlich zugänglich sind val
s. Dies ist nützlich, wenn Sie die Objekteigenschaften auf die gleiche Weise wie sie gespeichert werden möchten.
Auf der anderen Seite, wenn Sie die Interna Ihrer Objekte nicht offen legen wollen (also Ihre Klasse kann undurchsichtig genannt werden), um zum Beispiel einige Invarianten zu erzwingen, ist es vielleicht besser, Standard-Scala-Klassen zu verwenden .
Für allgemeine Regeln und Kompromisse im Zusammenhang mit Transparenz vs Opazität empfehle ich Li Haoyi Artikel Strategic Scala Style: Designing Datatypes lesen.
In einigen Fällen, zum Beispiel wenn Sie möchten, die Leistung verbessern oder Speicherbedarf Ihres Programms zu reduzieren, können Sie änderbare Klasse entscheiden, stattdessen zu verwenden:
class Door(var name: String,
var open: Boolean) {
def openDoor(newDoorState: Boolean): Unit =
open = newDoorState
}
val door = new Door("MyDoor",true)
door.name // "MyDoor"
door.open // true
door.openDoor(false)
door.name // "MyDoor"
door.open // false
Denken Sie daran, dass unveränderliches Objekt Im Allgemeinen bevorzugen Sie die Art und Weise, die Daten weiterzuleiten, und Sie sollten Ihre Programme standardmäßig als referenziell transparent gestalten. Wenn dies wirklich notwendig wird, sollten Sie solche Mikrooptimierungen wie die Änderung Ihrer Datenstrukturen als änderbar betrachten.
Auch ich werde hinzufügen, dass in Scala gibt es eine Konvention (die aus Java stammt), camel case für Klassenfelder und Methoden zu verwenden, so dass Sie Ihre Methode open_door
wie im ursprünglichen Beispiel benennen, sollten Sie es openDoor
nennen .
wäre es wert, dass die OP zu erwähnen sollte 'camelCase' für Methodennamen verwenden, weist darauf hin, dass es in der Regel besser ist, die zu verwenden erste Variante über die änderbare, drop vars, wenn er sowieso ein neues Objekt erstellen und wahrscheinlich eine 'case class Door (name: String, open: Boolean)' erstellen und 'open'-Methode mit' copy' implementieren will. –
Wie @ Łukasz oben erwähnt: 'Fall Klasse Tür (name: String, isOpen: Boolean) { def OPENDOOR (newDoorState: Boolean): Tür = this.copy (isOpen = newDoorState) }' –
Łukasz, Venkat Reddy Sudheer Aedama Es tut mir leid für die späte Antwort, das war in der Tat eine faule Antwort. Danke für deine Anregungen, ich habe meine Antwort jetzt mit mehr Details erweitert. – adamwy