2011-09-07 14 views
7

Il mio gioco hatipo di classe come chiave nella mappa a Scala

class Enemy 

chi è AI/funzionalità che posso cambiare con

trait Moving 
trait VerticalMover extends Moving 
trait RandomMover extends Moving 

e così via. Ora ho bisogno di recuperare cose precaricate basate su tratti. Quello che mi piacerebbe fare è avere una mappa che accetti tutti i tratti che estendono lo spostamento come chiavi e poi alcuni EnemyContainer come valore che dovrebbe avere contenuto correlato al tratto.

Ma come definire una tale mappa e come formattare my .get() per ottenere il contenitore da un'istanza di qualche nemico. Qualcosa di simile:

val myEnemy = new Enemy with RandomMover 
val myDetails:EnemyContainer = enemyDetailsStore.get(myEnemy.getClass) 
+0

Vuoi dire che 'enemyDetailsStore' restituisce una cosa se' MyEnemy' estende 'VerticalMover', e un'altra cosa se estende' RandomMover'? Cosa succede se estende entrambi? –

+0

Sì, questo è quello che intendevo. Ma sto iniziando a chiedermi per la sanità mentale di tutta la mia idea. Forse dovrei avere qualche stringa chiave incorporata nei tratti e usarla come chiave. Quindi, con la linearizzazione dei tratti, l'ultimo tratto dominante avrebbe impostato l'EnemyContainer in altre parole le trame utilizzate per visualizzare il nemico. – vertti

+3

La maggior parte delle volte, il punto di un tratto/interfaccia è dire "So come fare * X *", pur consentendo diverse implementazioni di X. In assenza di altri dettagli, avrei pensato che il design più naturale sarebbe avere il tratto 'Moving' ha una sorta di metodo' getMovingStrategy' o 'move' su di esso direttamente, che è possibile implementare di conseguenza nei sottotit del motore verticale e casuale. –

risposta

5

Bene, suppongo che i dati nemico negozio è di tipo Map[Class[_ <: Moving], EnemyDetails]. Ho il sospetto che qualcosa di simile:

//gives a Map[Class[_ <: Moving], EnemyDetails] for all matching keys 
enemyDetailsStore.filterKeys(_ isInstance myEnemy) 

Oppure:

//Iterable[EnemyDetails] 
enemyDetailsStore collect { case (c, d) if c isInstance myEnemy => d } 

O anche solo:

//Option[EnemyDetails] 
enemyDetailsStore collectFirst { case (c, d) if c isInstance myEnemy => d } 

farà per voi. L'unico "problema" con questo codice è che è O (N), in quanto richiede un attraversamento della mappa, piuttosto che una semplice ricerca, che sarebbe O (1) o O (log N)

10

Forse potresti avvolgere una mappa [Manifest, Any] assicurandoti che i valori corrispondano alle chiavi manifest.

Possibile schizzo di quello. Prima un piccolo aiutante

class Typed[A](value: A)(implicit val key: Manifest[A]) { 
    def toPair: (Manifest[_], Any) = (key, value) 
} 
object Typed { 
    implicit def toTyped[A: Manifest](a: A) = new Typed(a) 
    implicit def toTypable[A](a: A) = new { 
    def typedAs[T >: A : Manifest] = new Typed[T](a)(manifest[T]) 
    } 
} 

quindi l'involucro in sé (che non è una mappa)

class TypedMap private(val inner: Map[Manifest[_], Any]) { 
    def +[A](t: Typed[A]) = new TypedMap(inner + t.toPair) 
    def +[A : Manifest](a: A) = new TypedMap(inner + (manifest[A] -> a)) 
    def -[A : Manifest]() = new TypedMap(inner - manifest[A]) 
    def apply[A : Manifest]: A = inner(manifest[A]).asInstanceOf[A] 
    def get[A : Manifest]: Option[A] = inner.get(manifest[A]).map(_.asInstanceOf[A]) 
    override def toString = inner.toString 
    override def equals(other: Any) = other match { 
    case that: TypedMap => this.inner == that.inner 
    case _ => false 
    } 
    override def hashCode = inner.hashCode 
} 

object TypedMap { 
    val empty = new TypedMap(Map()) 
    def apply(items: Typed[_]*) = new TypedMap(Map(items.map(_.toPair) : _*)) 
} 

Con questo si può fare

import Typed._ 
val repository = TypedMap("foo", 12, "bar".typedAs[Any]) 

repository: TypedMap = Map (java .lang.String -> foo, Int -> 12, Any -> bar)

si recupera gli elementi con

repository[String] // returns "foo" 
repository.get[Any] // returns Some("bar") 

Credo che il costruttore privato deve garantire che il _ asInstanceOf è sicuro. inner può essere lasciato pubblico, in quanto è immutabile. In questo modo, la ricca interfaccia di Map sarà disponibile, ma sfortunatamente, non per creare un altro TypedMap.

Problemi correlati