2012-01-22 10 views
5

Tengo un grupo de clases (siguiendo el patrón de estrategia) en mi proyecto. En la función principal, recibo un valor enum del servidor y en base a eso creo un objeto del tipo de clase base.Cómo resolver el tipo de clase usando enum

Estoy utilizando el interruptor/declaración de caso para lograrlo. Leí en alguna parte que el principio Abierto/Cerrado no permite abrir una función para agregar una nueva declaración de caso cada vez que se agrega una nueva clase.

Estoy pensando en usar un Activator.CreateInstance(). ¿Hay algún inconveniente?

¿Hay alguna otra manera de crear un objeto a partir del tipo enum?

ejemplo Añadiendo a continuación a pesar de que no es un patrón Estrategia de pleno derecho

abstract public class Mammal 
{ 
    public abstract void MakeSound() 
} 

class Cat:Mammal 
{  
    public override void MakeSound() 
    { 
     Console.WriteLine("Meow");   
    }  
} 

class Dog:Mammal 
{ 

    public override void MakeSound() 
    { 
     Console.WriteLine("Bow");   
    }  
} 

Main() 
{ 

    MammalTypes mammalType = RecieveValueFromServer(); 
    Mammal mammalBase 
    switch(mammalType) // need to make this dynamic depending upon Enum type 
    { 
     case MammalTypes.Cat:mammalBase = new Cat() 
          break; 
     case MammalTypes.Dog:mammalBase = new Dog() 
          break;    
    } 

    mammalBase.MakeSound() 
} 
+2

** NO ** Objeto orientado. La clase base no debe ser consciente de sus clases derivadas. – gdoron

+1

Es difícil visualizar lo que has hecho. Por favor, agregue el código que ha escrito a su pregunta. – Abbas

Respuesta

1

Se puede usar un diccionario del tipo de enumeración a una función. Las funciones crea su objeto de la estrategia:

public delegate Strategy StrategyFactory(); 
var strategyFactories = new Dictionary<MyEnum, StrategyFactory>(); 

Este diccionario se usa para crear sus objetos basado en los valores de enumeración:

var newStategy = strategyFactories[myEnumValue](); 

la fábrica de funciones hay que añadir al diccionario de alguna manera. Para eso, puede exponer métodos de registro (y tal vez anular el registro).

0

Se puede crear un atributo que toma el tipo que el valor de enumeración representa y lo aplica al campo de enumeración de este modo:

enum MyEnum { 
    [Creates(typeof(FooStrategy))] 
    Foo, 
    [Creates(typeof(BarStrategy))] 
    Bar, 
    // etc. 
} 

[AttributeUsage(AttributeTargets.Field, Inherited=false, AllowMultiple=false)] 
sealed class CreatesAttribute : Attribute { 
    public Type TypeToCreate { get; private set; } 
    public CreatesAttribute(Type typeToCreate) { 
     TypeToCreate = typeToCreate; 
    } 

    public static IDictionary<T, Func<U>> GenerateLookup<T,U>() { 
     var query = from field in typeof(T).GetFields() 
        let creates = field.GetCustomAttriubtes(typeof(CreatesAttribute), false) as CreatesAttribute[] 
        let method = CreationMethod(typeof(U)) // create your type here 
        let key = (T)field.GetValue(null) 
        select new { Key = key, Method = method }; 
     return q.ToDictionary(item => item.Key, item => item.Method); 
    } 
} 

la parte izquierda hasta que es como se desea crear una instancia de Tu clase. Si todos tienen el mismo constructor, este método será fácil, ya que puede llamar al Type.GetConstructor(Type[]) y luego llamar al Invoke instancia ConstructorInfo, o puede usar un contenedor IoC y resolver una instancia del tipo, sin preocuparse demasiado por constructores con diferentes parámetros.

continuación, puede crear una clase estática para los métodos de extensión en su enumeración:

public static class MyEnumExtensions { 
    static readonly IDictionary<MyEnumType, MyBaseStrategyType> lookup = 
     CreatesAttribute.GenerateLookup<MyEnumType, MyBaseStrategyType>(); 

    public static MyBaseStrategyType CreateInstance(this MyEnumType key) { 
      return lookup[key](/* pass any common constructor values */); 
    } 
} 

Finalmente lo llamarían así:

var myEnum = MyEnumType.Foo; 
var strategy = myEnum.CreateInstance(); 
// use your strategy 

Esto debe evitar la violación de la apertura/cierre principio y le permite agregar tantas clases como desee y solo cambiar el Enum que puede ser lo suficientemente genérico como para crear una instancia de la estrategia directamente a partir del valor enum.

4

Un método para lograr una verdadera OCP podría ser el siguiente:

definir un método abstracto Is para forzar cada subtipo concreto de los Mamíferos para especificar si es apropiado para un determinado valor de la enumeración:

abstract public class Mammal 
{ 
    public abstract void MakeSound(); 

    public abstract bool Is(MammalTypes mammalType); 
} 

las implementaciones de está en las subclases se vería así:

class Cat : Mammal 
{ 
    // other specific members 

    public override bool Is(MammalTypes mammalType) 
    { 
     return mammalType == MammalTypes.Cat; 
    } 
} 

class Dog : Mammal 
{ 
    // other specific members 

    public override bool Is(MammalTypes mammalType) 
    { 
     return mammalType == MammalTypes.Dog; 
    } 
} 

hecho esto, ahora podemos crear un MammalF clase actory que, cuando se administra a un mamífero exploraciones valor de enumeración a través de las clases disponibles y, cuando se encuentra una coincidencia, devuelve una instancia de esa clase:

public class MammalFactory 
{ 
    private readonly IEnumerable<Type> _mammalTypes; 

    public MammalFactory() 
    { 
     var currentAssembly = Assembly.GetExecutingAssembly(); 

     _mammalTypes = currentAssembly.GetTypes() 
      .Where(t => typeof(Mammal).IsAssignableFrom(t) && !t.IsAbstract); 
    } 

    public Mammal Create(MammalTypes mammalType) 
    { 
     return _mammalTypes 
      .Select(type => CreateSpecific(type, mammalType)) 
      .First(mammal => mammal != null); 
    } 

    public Mammal CreateSpecific(Type type, MammalTypes mammalEnumType) 
    { 
     var mammalInstance = (Mammal)Activator.CreateInstance(type); 

     return mammalInstance.Is(mammalEnumType) ? mammalInstance : null; 
    } 
} 

El uso final se verá así:

var mammalFactory = new MammalFactory(); 

var guessWhatMammal = mammalFactory.Create(MammalTypes.Cat); 

Esto cumple completamente con OCP. Solo es necesario crear una nueva clase de Mamíferos para que se conecte automáticamente y esté lista para usar dentro de la aplicación. (No es necesario modificar nada en la aplicación, a excepción de la enumeración en sí)

Hay algunos problemas con este enfoque:

  • que sólo analiza el conjunto se está ejecutando actualmente para este tipo de mamíferos
  • tiene para crear una instancia de Mamíferos cada vez que se necesita para probar si ese tipo es apropiado

Aunque estos problemas pueden resolverse, uno está todavía queda: complejidad.

Esto es complejo porque:

  • hemos duplicado la cantidad de código necesario
  • la auto-cableado podría ser confuso para la gente nueva al proyecto

creo que la conclusión es esto: los patrones de diseño no son reglas estrictas. No vale la pena hacer algo solo para conformarse a un diseño dado. En su lugar, tenemos que ser pragmáticos y encontrar el equilibrio perfecto entre la conformidad del patrón y la utilidad/simplicidad/legibilidad. Esto depende en gran medida del problema que intentamos resolver y en muchos casos podría ser la declaración switch que presentó en la pregunta.

Cuestiones relacionadas