2012-01-22 15 views
5

Mam grupę klas (zgodnie ze wzorem strategii) w moim projekcie. W głównej funkcji otrzymuję wartość wyliczenia z serwera i na tej podstawie tworzę obiekt typu klasy bazowej.Jak rozwiązać typ klasy za pomocą enum

Używam instrukcji switch/case, aby to osiągnąć. Czytałem gdzieś, że zasada Open/Closed nie pozwala na otwarcie funkcji dodania nowej instrukcji case po dodaniu nowej klasy.

Mam na myśli użycie Activator.CreateInstance(). Czy jest jakaś wada?

Czy istnieje inny sposób utworzenia obiektu z typu wyliczeniowego?

Dodawanie przykład poniżej, mimo że nie jest pełnoprawnym Strategia wzór

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

To ** NIE ** Obiekt zorientowany. Klasa bazowa nie powinna być świadoma klas pochodnych. – gdoron

+1

Trudno wyobrazić sobie, co zrobiłeś. Dodaj kod, który napisałeś do swojego pytania. – Abbas

Odpowiedz

1

Można używać słownika z typu wyliczeniowego do funkcji. Funkcje tworzy swój przedmiot strategii:

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

Ten słownik używany do tworzenia obiektów na podstawie wartości ENUM:

var newStategy = strategyFactories[myEnumValue](); 

fabryka funkcje muszą być dodawane do słownika jakoś. W tym celu można ujawnić metody rejestracji (i być może wyrejestrować).

0

Można utworzyć atrybut, który zaczyna typ że wartość enum reprezentuje i zastosować go do pola enum tak:

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

Część lewo aż do was, jak chcesz utworzyć instancję Twoja klasa. Jeśli wszystkie mają ten sam konstruktor, wówczas ta metoda będzie łatwa, ponieważ można wywołać Type.GetConstructor(Type[]), a następnie wywołać Invoke instancję ConstructorInfo lub użyć kontenera IoC i rozwiązać instancję z typu, nie martwiąc się tak bardzo o konstruktory o różnych parametry.

Następnie można utworzyć statyczną klasę dla metod rozszerzenie na ENUM:

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

Wreszcie można by nazwać go tak:

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

ta powinna utrzymać się z naruszono otwarte/zamknięte zasady i pozwalają dodawać tyle klas, ile chcesz i zmieniają tylko Enum, które można uczynić generycznym na tyle, aby stworzyć instancję strategii bezpośrednio z wartości wyliczeniowej.

4

Jedną z metod osiągnięcia prawdziwej OCP mogą być następujące:

Zdefiniuj abstrakcyjną metodę Is zmusić każdy konkretny podtyp ssaka, aby określić, czy jest to odpowiednie dla danej wartości wyliczenia:

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

    public abstract bool Is(MammalTypes mammalType); 
} 

implementacje jest w podklasach wyglądałby następująco:

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

To zostało zrobione, możemy teraz stworzyć MammalF actory klasa, że ​​kiedy dany ssak skany ENUM wartosc poprzez dostępnych klas, a gdy znajdzie dopasowanie, zwraca instancję tej klasy:

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

Ostateczne wykorzystanie będzie wyglądać następująco:

var mammalFactory = new MammalFactory(); 

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

To w pełni zgodne z OCP. Konieczne jest tylko utworzenie nowej klasy Mammal, aby była automatycznie połączona i gotowa do użycia w aplikacji. (Nie ma potrzeby modyfikowania czegokolwiek innego we wniosku, z wyjątkiem samego wyliczenia)

istnieją pewne problemy z tego podejścia:

  • tylko skanuje aktualnie realizuje montaż dla typów ssak
  • ma aby utworzyć instancję ssaka za każdym razem musi sprawdzić, czy typ jest odpowiedni

Chociaż te problemy można rozwiązać, jedno pozostało: złożoność.

To skomplikowane, ponieważ:

  • mamy podwoiła ilość kodu potrzebna
  • auto-okablowanie może być mylące dla osób nowych w projekcie

myślę zawarcia jest to: wzorce projektowe nie są ścisłymi regułami. Nie warto robić czegoś tylko po to, aby dopasować się do danego projektu. Zamiast tego musimy być pragmatyczni i znaleźć idealną równowagę między zgodnością wzoru a użytecznością/prostotą/czytelnością. To w dużej mierze zależy od problemu, który próbujemy rozwiązać, aw wielu przypadkach może to być oświadczenie, które przedstawiłeś w pytaniu.

Powiązane problemy