Вот пример кода, над которым я работал, который использует подход возврата копии, а не изменения состояния напрямую. Хорошая особенность такого подхода, по крайней мере на стороне сервера, состоит в том, что он позволяет мне легко реализовать семантику типа транзакции. Если во время обновления что-то идет не так, для меня тривиально, чтобы все обновленное было в согласованном состоянии.
Приведенный ниже код взят с игрового сервера, на котором я работаю, который делает что-то похожее на то, что вы делаете, это для отслеживания объектов, которые перемещаются во временных интервалах. Этот подход не такой впечатляющий, как тот, который предлагает Дейв Гриффит, но он может пригодиться вам для размышлений.
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
}
Одна приятная вещь о case-классах в Scala заключается в том, что они создают для вас метод copy () - вы просто передаете, какие параметры были изменены, а остальные сохраняют то же значение. Вы можете кодировать это вручную, если вы не используете классы case, но вам нужно помнить, чтобы обновлять метод copy всякий раз, когда вы меняете значения, присутствующие в классе.
Что касается ресурсов, то для меня действительно важно было потратить некоторое время на выполнение задач в Эрланге, где, в сущности, нет иного выбора, кроме как использовать неизменное состояние. У меня есть две книги по Erlang, которые я изучил и тщательно изучил каждый пример. Благодаря тому, что я заставлял себя что-то делать в Erlang, мне стало намного удобнее работать с неизменяемыми данными.