2011-09-09 15 views
5

Si ayuda, la siguiente pregunta es en el contexto de un juego que estoy construyendo.¿Cuál es la forma correcta de reemplazar el bloque de interruptores y Enum en esta situación (C#)?

En algunos lugares diferentes tengo la siguiente situación. Existe una clase padre, para este ejemplo llamada Skill, y tengo una cantidad de subclases que implementan los métodos de la clase padre. También existe otra clase para padres a la que llamaremos Vocación. Las habilidades deben enumerarse en diferentes subclases de Vocación. Sin embargo, esas habilidades deben estar disponibles para cualquier cosa en el juego que use cualquier vocación dada.

Mi configuración actual es tener un Enum llamado Skill.Id, de modo que la Vocación contiene una colección de valores de ese Enum y cuando una entidad en el juego asume esa Vocación, la colección pasa a otra clase, llamada SkillFactory. Skill.Id necesita una nueva entrada cada vez que creo una nueva subclase Skill, así como un caso en el bloque switch para el constructor de las nuevas subclases.

es decir .:

//Skill.Id 
Enum{FireSkill,WaterSkill,etc} 

//SkillFactory 
public static Skill Create(Skill.Id id) 
{ 
    switch(id) 
    { 
     case Skill.Id.FireSkill: 
      return new FireSkill(); 
     //etc 
    } 
} 

Esto funciona perfectamente bien, pero utilizando la enumeración y el bloque de interruptores como un intermediario entre los siente como un mayor gasto de lo que necesito para resolver este problema. ¿Existe una forma más elegante de crear instancias de estas subclases de habilidades, pero aún permite que Vocation contenga una colección que identifique las habilidades que puede usar?

Editar: Estoy bien descartando el enum y el bloque de conmutadores asociado, siempre que Vocation pueda contener una colección que permita la creación de instancias arbitrarias de las subclases de Habilidad.

+0

Switch y lógica si las declaraciones son extremadamente rápidos, además de casi todos implementación de fábrica (que he visto) hace lo mismo que el uso un interruptor. En mi humilde opinión, no lo cambiaría. –

+0

Estoy de acuerdo con esto, siempre que no esté duplicando el cambio entre múltiples rutinas dentro del tipo. En ese punto, una búsqueda del diccionario es a menudo un mejor enfoque, como puede ponga el "interruptor" en una sola ubicación en el código. –

+0

esta es la forma más rápida y legible, ¿por qué debería encontrar otra manera? – ktutnik

Respuesta

8

Puede hacer un Dictionary<Skill.Id, Func<Skill>> y usarlo para crear instancias.

En el constructor:

Dictionary<Skill.Id, Func<Skill>> creationMethods = new Dictionary<Skill.Id, Func<Skill>>(); 
public SkillFactory() 
{ 
    creationMethods.Add(Skill.Id.FireSkill,() => new FireSkill()); 
    creationMethods.Add(Skill.Id.WaterSkill,() => new WaterSkill()); 
} 

Entonces, el método Create se convierte en:

public static Skill Create(Skill.Id id) 
{ 
    return creationMethods[id](); 
} 

Por supuesto, esto no es mucho mejor - excepto que permiten extender esto a otras funcionalidades eso es por ID sin duplicar el bloque de conmutadores si eso se convierte en un requisito. (Simplemente ponga más en el lado del valor del Diccionario.)

Dicho esto, a la larga, deshacerse por completo de la enumeración puede ser un buen beneficio para la extensibilidad. Esto requerirá un cambio más elaborado, sin embargo. Por ejemplo, si usó MEF, podría importar un conjunto de tipos SkillFactory en tiempo de ejecución y asociarlos a un nombre (a través de metadatos) a través de un único ImportMany. Esto le permitiría agregar nuevas subclases de habilidades sin cambiar su fábrica, y referirse a ellas por su nombre o algún otro mecanismo.

0

si esta función de creación va a ser tan usada que un "caso" producirá sobrecarga, el diccionario con claves enums generará una gran cantidad de basura.

En el contexto de un juego xna, puede ser peor que el "caso".

"Si utiliza un tipo de enumeración como una clave de diccionario, las operaciones internas del diccionario provocarán el encajonamiento. Puede evitar esto usando claves enteras, y convirtiendo sus valores enum a ints antes de agregarlos al diccionario."Extracted from here

Se puede utilizar una matriz simple enumeración y el reparto a int para la indexación:

Enum {FireSkill=0,WaterSkill=1,etc} 

Func<Skill>[] CreationMethods = new Func<Skill>() 
{ 
    () => new FireSkill(), 
    () => new WaterSkill(), 
} 
Cuestiones relacionadas