Disons que j'ai une classe de cas qui représente des personas, des personnes sur différents réseaux sociaux. Les instances de cette classe sont totalement immuables et font partie de collections immuables, qui seront éventuellement modifiées par un acteur Akka.
Maintenant, j'ai une classe de cas avec de nombreux champs et je reçois un message indiquant que je dois mettre à jour l'un des champs, quelque chose comme ceci:
case class Persona(serviceName : String,
serviceId : String,
sentMessages : Set[String])
// Somewhere deep in an actor
val newPersona = Persona(existingPersona.serviceName,
existingPersona.serviceId,
existingPersona.sentMessages + newMessage)
Notez que je dois spécifier tous les champs, même si un seul change. Existe-t-il un moyen de cloner l’utilisateur existant et de ne remplacer qu’un seul champ sans spécifier tous les champs qui ne changent pas? Puis-je écrire cela comme un trait et l'utiliser pour toutes mes classes de cas?
Si Persona était une instance de type Map, ce serait facile à faire.
case class
comes avec une méthode copy
dédiée à cet usage:
val newPersona = existingPersona.copy(sentMessages =
existingPersona.sentMessages + newMessage)
Depuis la 2.8, Scala ont une méthode copy
qui tire parti des paramètres nommés/par défaut pour exploiter sa magie:
val newPersona =
existingPersona.copy(sentMessages = existing.sentMessages + newMessage)
Vous pouvez également créer une méthode sur Persona
pour simplifier l'utilisation:
case class Persona(
svcName : String,
svcId : String,
sentMsgs : Set[String]
) {
def plusMsg(msg: String) = this.copy(sentMsgs = this.sentMsgs + msg)
}
ensuite
val newPersona = existingPersona plusMsg newMsg
existingPersona.copy(sentMessages = existingPersona.sentMessages + newMessage)
Je ne voulais pas inclure une grande bibliothèque pour faire des objectifs complexes qui vous permettent de définir des valeurs profondes dans des classes de cas imbriquées. Il s'avère que ce ne sont que quelques lignes de code dans la bibliothèque scalaz:
/** http://stackoverflow.com/a/5597750/329496 */
case class Lens[A, B](get: A => B, set: (A, B) => A) extends ((A) => B) with Immutable {
def apply(whole: A): B = get(whole)
def mod(a: A, f: B => B) = set(a, f(this (a)))
def compose[C](that: Lens[C, A]) = Lens[C, B](
c => this(that(c)),
(c, b) => that.mod(c, set(_, b))
)
def andThen[C](that: Lens[B, C]) = that compose this
}
Vous pouvez ensuite créer des objectifs qui définissent des valeurs profondément imbriquées beaucoup plus facilement que d'utiliser la fonction de copie intégrée. Voici un lien vers un grand ensemble d'objectifs complexes que ma bibliothèque utilise pour définir des valeurs fortement imbriquées.
Pensez à utiliser lens
dans la bibliothèque Shapeless
:
import shapeless.lens
case class Persona(serviceName : String,
serviceId : String,
sentMessages : Set[String])
// define the lens
val messageLens = lens[Persona] >> 'sentMessages
val existingPersona = Persona("store", "Apple", Set("iPhone"))
// When you need the new copy, by setting the value,
val newPersona1 = messageLens.set(existingPersona)(Set.empty)
// or by other operation based on current value.
val newPersona2 = messageLens.modify(existingPersona)(_ + "iPad")
// Results:
// newPersona1: Persona(store,Apple,Set())
// newPersona2: Persona(store,Apple,Set(iPhone, iPad))
De plus, si vous avez imbriqué, les classes getter
et setter
Les méthodes peuvent être un peu fastidieuses à composer. Ce sera une bonne occasion de simplifier en utilisant la bibliothèque de lentilles.
Veuillez également vous référer à: