I'd like to be able to assemble domain objects from traits, according to various properties that the concrete classes might have. When my objects are mutable, this is pretty straightforward. For example:
trait HasHitPoints { var hitPoints: Int = 100 }
trait HasBearing { var bearing: Double = 0 }
class Ship extends HasHitPoints with HasBearing
class Base extends HasHitPoints
val entities = new Ship :: new Base :: Nil
entities.collect { case h: HasHitPoints => h.hitPoints += 10 }
In particular, I can polymorphically read or update any HasHitPoints
instance without knowing the concrete type.
What is the best way to implement this with immutable objects? If I'm happy to just read the properties, then I could do something like:
trait HasHitPoints { val hitPoints: Int }
trait HasBearing { val bearing: Double }
case class Ship(hitPoints: Int, bearing: Double) extends HasHitPoints with HasBearing
case class Base(hitPoints: Int) extends HasHitPoints
val things = Ship(50, 0) :: Base(100) :: Nil
val totalHitPoints = things.collect { case h: HasHitPoints => h.hitPoints }.sum
Also, I can easily modify the concrete classes using copy
if I know the precise type. The hard part is updating an arbitrary HasHitPoints
, for example. If I have lots of concrete classes, and lots of different properties I might like to mix-in, what's the best scheme to avoid an explosion of boilerplate code?