2012-01-22 34 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聲明。