2013-07-09 17 views
7

con un oggetto mutabile posso scrivere qualcosa di simileScala "Aggiorna" immutabili oggetti migliori pratiche

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

Se l'utente è immutabile poi ho bisogno di clonare \ copiarlo su ogni operazione di modifica.

So alcuni modi per eseguire questa

  1. caso classe copia
  2. metodo (come changename), che crea un nuovo oggetto con la nuova proprietà

Qual è la migliore pratica?

E un'altra domanda. Esiste una tecnica esistente per ottenere "modifiche" rispetto all'oggetto originale (ad esempio per generare una dichiarazione di aggiornamento)?

+0

Per ottenere le modifiche, è possibile utilizzare Event Sourcing. –

risposta

8

Entrambi i modi che hai menzionato appartengono rispettivamente ai paradigmi funzionali e OO. Se si preferisce la scomposizione funzionale con il tipo di dati astratti, che, in Scala, è rappresentato da classi del caso, quindi scegliere il metodo di copia. L'uso dei mutatori non è una buona pratica nella mia opzione, perché ciò ti riporterà al modo di vita Java/C#/C++.

D'altra parte rendendo classe caso ADT come

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

è più conciso poi:

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

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

(non il migliore codice imperativo, può essere più breve, ma chiaro).

Di causa c'è un altro modo con mutatori, solo per restituire un nuovo oggetto per ogni chiamata:

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

Ma ancora modo caso di classe è più conciso.

E se andate oltre, alla programmazione simultanea/parallela, vedrete che il concetto funzionale con valore immutabile è molto meglio, quindi cercate di indovinare in quale stato il vostro oggetto è attualmente.

Aggiornamento

Grazie alla Senia, dimenticato di citare due cose.

Lenti
Al livello più elementare, le lenti sono una sorta di getter e setter per i dati immutabili e si presenta in questo modo:

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

che è. Un obiettivo è un oggetto che contiene due funzioni: ottenere e impostare. prende un A e restituisce un B. set prende un A e B e restituisce un nuovo A. È facile vedere che il tipo B è un valore contenuto in A. Quando passiamo un'istanza per ottenere restituiamo quel valore. Quando passiamo un A e un B per impostare aggiorniamo il valore B in A e restituiamo un nuovo A che riflette il cambiamento. Per praticità, l'applicazione è alias da applicare.C'è una buona intro per Scalaz Lens caso classe

Records
Questo, ofcause, proviene dalla biblioteca shapeless e chiamati Records. Un'implementazione di record estensibili modellati come HLists di associazioni. Le chiavi sono codificate utilizzando i tipi singleton e determinano completamente i tipi dei loro valori corrispondenti (ex 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

È possibile aggiungere la terza via alla risposta: [obiettivi] (https://blog.stackmob.com/2012/02/an-introduction-to-lenses-in-scalaz/). Non è abbastanza per una nuova risposta, ma vale la pena menzionarla. – senia

+0

@senia grazie agli obiettivi aggiunti e ai record informi – 4lex1v