I had a problem modeling our domain with immutable objects.
Unrelated design
One basic feature of WObject (world object) and many features for implementing certain operations, such as OwnedObj (hp / owner / takeDamage), Movable (motionLeft / moveTo), Fighter (attack / attack).
At the end of the hierarchy, you have a mutable class that mixes with the corresponding attributes:
class Corvette(var position: Vect2) extends WObject with OwnedObj with Movable with Fighter
If the user wants to perform an operation (say, move a ship), follow these steps:
val opt = objects.collectFirst { case obj: Movable if obj.position == position => obj }
opt.fold(Log.error(s"Movable at $position not found!")) { obj =>
obj.moveTo(position) // return type is Unit
}
Unrivaled design
If moveTo should return a new object, what type does it return?
trait Movable[Self <: Movable[Self]], Movable [_] , - . , Movable[_] with Fighter[_]? ?
Self bounds, :
def takeDamage(obj: OwnedObj): obj.Self = if (Random.nextDouble()) obj.takeDamage else obj.self
, ,
def attackReachable(
data: WObject.WorldObjUpdate[Self]
): WObject.WorldObjUpdate[data.value._2.Self]
.
, + , , .
:
case class WObject(position: Vect2, id: UUID=UUID.randomUUID())
case class OwnedObj(owner: Owner)
case class Movable(movementLeft: Int)
case class Fighter(attacked: Boolean)
case class Corvette(obj: WObject, owned: OwnedObj, movable: Movable, fighter: Fighter)
trait MovableOps[A <: ???] {
def moveTo(obj: A, target: Vect2): A
}
, Corvette.
, .
, ?
val opt = objects.collectFirst { case obj: ??? if obj.position == position => obj }
opt.fold(Log.error(s"Movable at $position not found!")) { obj =>
objects = objects - obj + obj.moveTo(position)
}
:)
: