2011-09-07 20 views
7

Mi juego tienetipo de clase como clave en el mapa de Scala

class Enemy 

que está AI/funcionalidad que puede cambiar con

trait Moving 
trait VerticalMover extends Moving 
trait RandomMover extends Moving 

y así sucesivamente. Ahora necesito buscar elementos precargados en función del rasgo. Lo que me gustaría hacer es tener un mapa que acepte todos los rasgos que extienden mover como claves y luego un poco de EnemyContainer como valor que tendría el contenido relacionado con el rasgo precargado.

Pero, ¿cómo puedo definir un Mapa así y cómo formatear mi .get() para obtener el contenedor por una instancia de algún Enemigo? Algo así como:

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

¿Quiere decir que quiere que 'enemyDetailsStore' devuelva una cosa si' myEnemy' se extiende 'VerticalMover', y otra cosa si se extiende' RandomMover'? ¿Qué pasa si se extiende a los dos? –

+0

Sí, eso es lo que quise decir. Pero estoy empezando a preguntarme por la cordura de mi idea. Tal vez debería tener alguna cadena clave incrustada en rasgos y usarla como la clave. Entonces, con la linealización del rasgo, el último rasgo primordial establecería el EnemyContainer en otras palabras, las texturas utilizadas para mostrar al enemigo. – vertti

+3

La mayor parte del tiempo, el punto de un rasgo/interfaz es decir "Sé cómo hacer * X *", mientras que permite diferentes implementaciones de X. A falta de otros detalles, habría pensado que el diseño más natural sería sea ​​para que el rasgo 'Moving 'tenga algún tipo de método' getMovingStrategy' o 'move' directamente sobre él, que puede implementarse en consecuencia en los subtrazos verticales y aleatorios del elemento motriz. –

Respuesta

5

Bueno, supongo que su tienda de detalles enemigas es de tipo Map[Class[_ <: Moving], EnemyDetails]. Sospecho que algo como:

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

O:

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

O incluso:

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

hará por ti. El único "problema" con este código es que es O (N), ya que requiere un recorrido del mapa, en lugar de una búsqueda simple, que sería O (1) o O (log N)

10

Tal vez podría envolver un Mapa [Manifiesto, Cualquiera] asegurando que los valores corresponden a las claves de manifiesto.

Posible boceto de eso. Primero un poco de ayuda

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]) 
    } 
} 

entonces el envoltorio en sí (que no es un mapa)

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 eso se puede hacer

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

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

a recuperar elementos con

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

Creo que el constructor privado debe garantizar que la _ asInstanceOf es seguro. inner puede dejarse en el público, ya que es inmutable. De esta forma, la rica interfaz de Map estará disponible, pero lamentablemente no se creará otra TypedMap.

Cuestiones relacionadas