Подтвердить что ты не робот

Полиморфные обновления в неизменяемой иерархии классов

Я хотел бы иметь возможность собирать объекты домена из признаков, в зависимости от различных свойств, которые могут иметь конкретные классы. Когда мои объекты изменяемы, это довольно просто. Например:

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 }

В частности, я могу полиморфно читать или обновлять любой экземпляр HasHitPoints, не зная конкретного типа.

Каков наилучший способ реализовать это с неизменяемыми объектами? Если я рад просто прочитать свойства, тогда я мог бы сделать что-то вроде:

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

Кроме того, я могу легко изменить конкретные классы, используя copy, если я знаю точный тип. Например, жесткая часть обновляет произвольное HasHitPoints. Если у меня много конкретных классов и множество разных свойств, которые я мог бы использовать для смешивания, какая лучшая схема, чтобы избежать взлома кода шаблона?

4b9b3361

Ответ 1

Возможно, вам повезло с добавлением, например, abstract def withHitPoints (points: Int) метод к вашим чертам, который возвращает копию объекта-контейнера с другим значением свойства. Это уменьшает использование до чего-то вроде:

val damagedActors = actors map { actor => actor.withHitPoints( actor.hitPoints - 10 ) }

Но в противном случае потребуется дополнительный метод для каждого свойства для конкретного класса, поэтому я не уверен, что он действительно решает вашу проблему. Это не кажется правильным для статического языка, такого как Scala (и я, вероятно, не буду настаивать на неизменности этого конкретного случая использования); непреложное решение здесь может быть лучшим кандидатом на динамический язык.

Ответ 2

Вы надеетесь избежать методов N обновления в конкретных классах M. Как бы болезненно это ни казалось, это невозможно. Вам понадобится доступ к методу копирования или по крайней мере конструктору каждого конкретного класса. Ни один из них не может быть абстрагирован, как это также обсуждается в: абстракция метода класса экземпляра класса() Таким образом, в конце концов вы всегда будете иметь код шаблона N x M.