Aquí hay una muestra de algún código en el que he estado trabajando que utiliza el enfoque de devolver una copia en lugar de mutar el estado directamente.Lo bueno de este tipo de enfoque, por lo menos en el lado del servidor, es que me permite implementar fácilmente la semántica del tipo de transacción. Si algo sale mal al hacer una actualización, es trivial para mí tener todo lo que se actualizó en un estado consistente.
El siguiente código es de un servidor de juegos en el que estoy trabajando, que hace algo similar a lo que estás haciendo, es para rastrear objetos que se mueven en segmentos de tiempo. Este enfoque no es tan espectacular como sugiere Dave Griffith, pero puede ser útil para la contemplación.
case class PosController(
pos: Vector3 = Vector3.zero,
maxSpeed: Int = 90,
velocity: Vector3 = Vector3.zero,
target: Vector3 = Vector3.zero
) {
def moving = !velocity.isZero
def update(elapsed: Double) = {
if (!moving)
this
else {
val proposedMove = velocity * elapsed
// If we're about to overshoot, then stop at the exact position.
if (proposedMove.mag2 > pos.dist2(target))
copy(velocity = Vector3.zero, pos = target)
else
copy(pos = pos + proposedMove)
}
}
def setTarget(p: Vector3) = {
if (p == pos)
this
else {
// For now, go immediately to max velocity in the correct direction.
val direction = (p - pos).norm
val newVel = direction * maxSpeed
copy(velocity = direction * maxSpeed, target = p)
}
}
def setTargetRange(p: Vector3, range: Double) = {
val delta = p - pos
// Already in range?
if (delta.mag2 < range * range)
this
else {
// We're not in range. Select a spot on a line between them and us, at max range.
val d = delta.norm * range
setTarget(p - d)
}
}
def eta = if (!moving) 0.0 else pos.dist(target)/maxSpeed
}
Una cosa buena acerca de las clases de casos en Scala es que crean el método de copia() para usted-- que acaba de pasar en la que los parámetros han cambiado, y los otros conservan el mismo valor. Puede codificar esto a mano si no está utilizando clases de casos, pero debe recordar actualizar el método de copia siempre que cambie los valores que están presentes en la clase.
En cuanto a los recursos, lo que realmente hizo una diferencia para mí fue pasar algo de tiempo haciendo cosas en Erlang, donde básicamente no hay más remedio que usar el estado inmutable. Tengo dos libros de Erlang que trabajé y estudié cuidadosamente cada ejemplo. Eso, además de forzarme a hacer algunas cosas en Erlang me hizo sentir mucho más cómodo trabajando con datos inmutables.
El consenso general (entre todas las respuestas) parece ser simplemente crear copias de mis objetos que representan el estado actual. Esto no es exactamente lo que estaba buscando, pero esta respuesta presenta algunos elementos de pensamiento realmente interesantes, como la definición de iteradores que expresan cómo cambian los valores (en lugar de expresar el estado de los valores) ... Esperaba una respuesta que encuentre alguna forma de expresar la posición como algún tipo de función (o iteración) que exprese cómo cambia la variable (como una integral en el cálculo). ¡Gracias por haberme introducido algunas cosas interesantes! – jtb