2013-07-09 12 views
7

Mit einem veränderlichen Objekt kann ich so etwas wieScala „update“ unveränderliches Objekt Best Practices

var user = DAO.getUser(id) 
user.name = "John" 
user.email ="[email protected]" 
// logic on user 

schreibt Wenn Benutzer unveränderlich ist, dann muß ich \ klonen kopieren Sie es auf jedem Wechselvorgang.

Ich kenne ein paar Möglichkeiten, diese Kopie

  1. Fallklasse
  2. Methode (wie Change), die

Was ist die beste Praxis ein neues Objekt mit der neuen Eigenschaft schafft auszuführen?

Und noch eine Frage. Gibt es eine vorhandene Technik, um "Änderungen" relativ zum ursprünglichen Objekt zu erhalten (zum Beispiel um update statement zu generieren)?

+0

Um die Änderungen zu erhalten, können Sie Event Sourcing verwenden. –

Antwort

8

Beide Wege, die Sie erwähnt haben, gehört zu funktionalen bzw. OO-Paradigmen. Wenn Sie die funktionale Dekomposition mit dem abstrakten Datentyp bevorzugen, der in Scala durch Fallklassen repräsentiert wird, dann wählen Sie die Kopiermethode. Mutatoren zu verwenden, ist keine gute Übung in meiner Wahl, denn das wird dich zurück in die Lebensweise von Java/C#/C++ ziehen.

Auf der anderen Seite ADT Fall Klasse machen wie

case class Person(name: String, age: String) 

dann mehr consise ist:

class Person(_name: String, _age: String) { 
    var name = _name 
    var age = _a 

    def changeName(newName: String): Unit = { name = newName } 
    // ... and so on 
} 

(nicht der beste Imperativ Code kann kürzer, aber klar sein).

Von der Ursache gibt es eine andere Art und Weise mit Mutatoren, nur bei jedem Aufruf ein neues Objekt zurückgeben:

class Person(val name: String, 
      val age: String) {  
    def changeName(newName: String): Unit = new Person(newName, age) 
    // ... and so on 
} 

Aber noch Fall-Klasse Art und Weise mehr consise ist.

Und wenn Sie weiter gehen, parallel/parallele Programmierung, werden Sie sehen, dass funktionale Konzept mit unveränderlichen Wert sind viel besser, dann tring zu raten, in welchem ​​Zustand Ihr Objekt derzeit sind.

aktualisieren

Dank der senia, vergaßen zwei Dinge zu erwähnen.

Objektive
Auf der untersten Ebene, Linsen sind eine Art von Getter und Setter für unveränderlichen Daten und sieht wie folgt aus:

case class Lens[A,B](get: A => B, set: (A,B) => A) { 
    def apply(a: A) = get(a) 
    // ... 
} 

dass es ist. Ein Objektiv ist ein Objekt, das zwei Funktionen enthält: get und set. get nimmt ein A und gibt ein B zurück. set nimmt ein A und B und gibt ein neues A zurück. Es ist leicht zu sehen, dass der Typ B ein Wert in A ist. Wenn wir eine Instanz übergeben, erhalten wir diesen Wert zurück. Wenn wir ein A und ein B zum Setzen übergeben, aktualisieren wir den Wert B in A und geben ein neues A zurück, das die Änderung widerspiegelt. Der Einfachheit halber wird der Alias ​​verwendet, um anzuwenden.Es gibt eine gute intro zu Scalaz Objektiv Fall Klasse

Aufzeichnungen
Dieser, ofcause, aus der shapeless Bibliothek kommt und rief Datensätze. Eine Implementierung von erweiterbaren Datensätzen, modelliert als HLists of associations. Die Schlüssel werden mit Singletontypen codiert und bestimmen vollständig die Typen der entsprechenden Werte (z. B. von GitHub):

object author extends Field[String] 
object title extends Field[String] 
object price extends Field[Double] 
object inPrint extends Field[Boolean] 

val book = 
    (author -> "Benjamin Pierce") :: 
    (title -> "Types and Programming Languages") :: 
    (price -> 44.11) :: 
    HNil 

// Read price field 
val currentPrice = book.get(price) // Inferred type is Double 
currentPrice == 44.11 

// Update price field, relying on static type of currentPrice 
val updated = book + (price -> (currentPrice+2.0)) 

// Add a new field 
val extended = updated + (inPrint -> true) 
+3

Sie können den dritten Weg zu Ihrer Antwort hinzufügen: [Linsen] (https://blog.stackmob.com/2012/02/an-introduction-to-lenses-in-scalaz/). Es ist nicht genug für eine neue Antwort, aber es ist erwähnenswert. – senia

+0

@senia dank Linsen und formlosen Aufzeichnungen hinzugefügt – 4lex1v