2012-01-10 11 views
17

Me gustaría poder ensamblar objetos de dominio a partir de rasgos, de acuerdo con varias propiedades que las clases concretas puedan tener. Cuando mis objetos son mutables, esto es bastante sencillo. Por ejemplo:Actualizaciones polimórficas en una jerarquía de clase inmutable

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 } 

En particular, puedo leer polimórfica o actualizar cualquier HasHitPoints instancia sin conocer el tipo de hormigón.

¿Cuál es la mejor manera de implementar esto con objetos inmutables? Si estoy feliz de leer simplemente las propiedades, entonces yo podría hacer algo como:

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 

Además, puedo modificar fácilmente las clases concretas utilizando copy si conozco el tipo preciso. La parte difícil es actualizar un arbitrario HasHitPoints, por ejemplo. Si tengo muchas clases concretas y muchas propiedades diferentes que me gustaría mezclar, ¿cuál es el mejor esquema para evitar una explosión de código repetitivo?

Respuesta

1

Puede tener un poco de suerte al agregar una p. Ej. defina de forma abstracta el método HitHints (points: Int) con sus características, que devuelve una copia del objeto contenedor con un valor de propiedad diferente. Esto reduce el uso de algo como:

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

Pero será de otro modo requeriría un método adicional por propiedad por clase concreta, así que no estoy seguro de lo que realmente resuelve su problema. Esto no se siente bien para un lenguaje estático como Scala (ni me molestaría con la inmutabilidad para este caso de uso en particular); una solución inmutable aquí puede ser un mejor candidato para un lenguaje dinámico.

+0

Correcto, aunque el desorden de N métodos de actualización en M clases concretas era lo que esperaba evitar. Y como usted señala, sería bastante sencillo en un lenguaje dinámico. Este caso de uso es, obviamente, muy simplificado, pero creo que el problema a menudo surge cuando se mezclan las actualizaciones con una jerarquía de herencia inmutable. –

+0

No argumentaré que es algo que Scala no maneja bien, pero en mi experiencia es un escenario que no aparece a menudo, a menos que, por supuesto, estés muerto en este momento del modelado. –

+2

Posiblemente, aunque me pregunto hasta qué punto esa es la cuestión de Sapir-Whorf: los escenarios que un lenguaje no puede manejar bien se convierten en los escenarios que no surgen a menudo ... –

1

Esperas evitar los N métodos de actualización en M clases concretas. Por doloroso que sea, creo que esto simplemente no es posible. Necesitará acceso al método de copia o al menos al constructor de cada clase concreta. Ninguno de ellos puede ser abstraído como también se discute en: Case class copy() method abstraction Así que al final siempre se terminará con el código N x M 'repetitivo'.

Cuestiones relacionadas