2012-01-22 24 views
5

我的项目中有一组类(以下是策略模式)。在主函数中,我从服务器收到一个枚举值,并基于该值创建基类类型的对象。如何使用枚举解决类的类型

我正在使用switch/case语句来实现此目的。我在某处读到Open/Closed原理不允许在添加新类时打开添加新的case语句的函数。

我正在考虑使用Activator.CreateInstance()。有没有什么缺点。

是否有任何其他方式从枚举类型创建对象?

下面的添加,即使它不是一个完全成熟的策略模式

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

这是** NOT **面向对象。基类不应该知道它的派生类。 – gdoron

+1

很难想象你做了什么。请添加您写到您的问题的代码。 – Abbas

回答

1

你可以使用字典从枚举类型的函数的例子。该功能创建自己的战略目标:

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

这本词典使用基于枚举值来创建对象:

var newStategy = strategyFactories[myEnumValue](); 

工厂的功能需要被添加到字典中莫名其妙。为此,您可以公开注册(也可能取消注册)方法。

0

您可以创建一个属性,它需要的类型枚举值表示,并将其应用到枚举领域,像这样:

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

的部分留给你的就是你要如何创建一个实例你的班。如果它们都具有相同的构造函数,那么这种方法将很容易,因为您可以调用Type.GetConstructor(Type[]),然后调用InvokeConstructorInfo实例,或者您可以使用IoC容器并从类型中解析实例,而不必担心具有不同类型的构造函数参数。

然后你就可以在你的枚举创建扩展方法的静态类:

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 */); 
    } 
} 

最后,你会调用它像这样:

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

这应该让你从违反开启/关闭的原则,并允许您添加尽可能多的类,只需要改变Enum,使其足够通用即可直接从枚举值创建策略实例。

4

实现真正的OCP可能是以下一种方法:

定义的抽象方法Is强制哺乳动物的每一个具体亚型指定是否它是适合枚举的给定值:

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

    public abstract bool Is(MammalTypes mammalType); 
} 

在子类中是实现会是什么样子:

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; 
    } 
} 

这个正在做,我们现在可以创建一个MammalF

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; 
    } 
} 

最终的使用将是这样的:

var mammalFactory = new MammalFactory(); 

var guessWhatMammal = mammalFactory.Create(MammalTypes.Cat); 
,通过现有的类,当它找到一个匹配给予哺乳动物枚举值扫描时,它返回类的一个实例actory类

这完全符合OCP。只需要创建一个新的Mammal类,它就可以自动连线并准备在应用程序中使用。 (无需修改任何其他应用程序中,除了枚举本身)

有一些问题,这种方法:

  • 它只扫描哺乳动物类型的当前执行的程序集
  • 它有打造哺乳动物的实例每次需要测试类型是否适合

虽然这些问题可以得到解决的时候,一个仍留有:复杂

这是复杂的,因为:

  • 我们已经增加了一倍的代码量需要
  • 自动布线可能会混淆人们新的项目

我认为结论是这样的:设计模式不是严格的规则。仅仅为了符合给定的设计,不值得做某件事。相反,我们必须务实,并且发现模式一致性和有用性/简单性/可读性之间的完美平衡。这很大程度上取决于我们试图解决的问题,在很多情况下,这很可能是您在问题中提出的switch声明。