2012-01-10 10 views
17

Mi piacerebbe essere in grado di assemblare oggetti di dominio da tratti, in base alle varie proprietà che potrebbero avere le classi concrete. Quando i miei oggetti sono mutabili, questo è abbastanza semplice. Per esempio:Aggiornamenti polimorfici in una gerarchia di classe immutabile

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 particolare, posso polimorfico leggere o aggiornare qualsiasi HasHitPoints esempio senza conoscere il tipo concreto.

Qual è il modo migliore per implementare questo con oggetti immutabili? Se io sono felice di leggere solo le proprietà, allora avrei potuto fare qualcosa di simile:

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 

Inoltre, posso modificare facilmente le classi concrete utilizzando copy se conosco il tipo preciso. La parte difficile è l'aggiornamento di un arbitrario HasHitPoints, ad esempio. Se ho un sacco di lezioni concrete e molte proprietà diverse che mi piacerebbe mescolare, qual è il miglior schema per evitare un'esplosione di codice boilerplate?

risposta

1

Si può avere un po 'di fortuna con l'aggiunta di un es. abstract def with HitPoints (points: Int) metodo per i tratti, che restituisce una copia dell'oggetto contenitore con un valore di proprietà diverso. Questo riduce l'utilizzo a qualcosa di simile:

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

Ma sarà comunque necessario un metodo in più per immobile a classe concreta, quindi non sono sicuro che risolve davvero il problema. Questo non sembra giusto per un linguaggio statico come Scala (né mi verrebbe più in mente l'immutabilità per questo particolare caso d'uso); una soluzione immutabile qui potrebbe essere un candidato migliore per un linguaggio dinamico.

+0

Giusto, anche se il disordine dei metodi di aggiornamento N nelle classi concrete M era quello che speravo di evitare. E come fai notare, sarebbe piuttosto semplice in un linguaggio dinamico. Questo caso d'uso è ovviamente molto semplificato, ma penso che spesso il problema si risolva quando si mescolano gli aggiornamenti con una gerarchia di ereditarietà immutabile. –

+0

Non dirò che è qualcosa che Scala non gestisce bene, ma nella mia esperienza è uno scenario che non emerge spesso - a meno che, naturalmente, non siate morti in questo momento della modellazione. –

+2

Forse, anche se mi chiedo fino a che punto è la cosa di Sapir-Whorf: gli scenari che un linguaggio non può gestire bene diventano gli scenari che non si presentano spesso ... –

1

Si spera di evitare i metodi di aggiornamento N nelle classi concrete M. Per quanto penoso, penso che questo non sia possibile. Avrai bisogno di accedere al metodo di copia o almeno al costruttore di ogni classe concreta. Nessuno dei due può essere astratto, come è anche discusso in: Case class copy() method abstraction Quindi alla fine si finisce sempre con il codice N 'M' boilerplate '.

Problemi correlati