2012-01-22 10 views
5

私は、私のプロジェクトにクラスのグループ(以下の戦略パターン)を持っています。 main関数では、サーバーからenum値を受け取り、それに基づいて基本クラス型のオブジェクトを作成します。enumを使用してクラスの型を解決する方法

私は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

** **はオブジェクト指向ではありません。基本クラスは、それが派生クラスであることを認識すべきではありません。 – gdoron

+1

あなたがしたことを視覚化するのは難しいです。あなたの質問に書いたコードを追加してください。 – Abbas

答えて

1

ではないにもかかわらず、以下の例を追加。機能は、あなたの戦略オブジェクトを作成します。

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

列挙値に基づいてオブジェクトを作成するために使用されるこの辞書:

var newStategy = strategyFactories[myEnumValue](); 

ファクトリ関数は、何らかの形で辞書に追加する必要があります。そのために、登録メソッドを公開したり、登録解除することができます。

0

あなたは、列挙型の値が表す型を取る属性を作成し、そのようにenumフィールドに適用できます。

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コンテナを使用して型からインスタンスを解決することができます。パラメーター。

次に、あなたがあなたの列挙型の拡張メソッドのための静的クラスを作成することができます。

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

これは、オープン/クローズ違反からあなたを保つ必要がありますので、同じよう

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

最後に、あなたがそれを呼ぶだろう原則として、あなたが望むだけ多くのクラスを追加することができ、列挙型の値を直接変更して列挙型の値から直接戦略のインスタンスを作成することができます。

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クラスを作成する必要があります。 (列挙型自体を除いて、アプリケーション内で何かを修正する必要)

このアプローチにはいくつかの問題がありません:

  • はそれだけで、それが持っている哺乳類の種類
  • のために現在実行中のアセンブリをスキャン哺乳動物のそれは、そのタイプは、これらの問題に対処することができますが

適切であるかどうかをテストする必要があるたびにインスタンスを作成するには、1がまだ残っている:複雑を。

ので、これは複雑です:私たちはコードの量を倍増してきました

  • は、自動配線がプロジェクト

に新しい人々のために混乱するかもしれません

  • 私が結論を考える必要これは、デザインパターンは厳密なルールではありません。与えられたデザインに合致するだけの価値があるわけではありません。 代わりに、私たちは実用的でなければならず、パターン適合性と有用性/簡潔性/可読性との間の完全なバランスを見つけなければならない。これは、私たちが解決しようとする問題に大きく依存しています。多くの場合、あなたが質問で提示したswitchステートメントである可能性があります。

  • 関連する問題