2012-01-22 33 views
5

Tôi có một nhóm các lớp (theo mẫu chiến lược) trong dự án của tôi. Trong hàm main, tôi nhận được một giá trị enum từ máy chủ và dựa trên đó tôi tạo một đối tượng của kiểu lớp cơ sở.Cách giải quyết loại lớp bằng cách sử dụng enum

Tôi đang sử dụng câu lệnh chuyển/trường hợp để đạt được điều này. Tôi đọc ở đâu đó rằng nguyên tắc Open/Closed không cho phép mở một hàm để thêm một câu lệnh case mới bất cứ khi nào một lớp mới được thêm vào.

Tôi đang nghĩ đến việc sử dụng Activator.CreateInstance(). Có bất kỳ hạn chế nào đối với nó không.

Có cách nào khác để tạo đối tượng từ loại enum không?

Thêm ví dụ dưới đây mặc dù nó không phải là một chiến lược chính thức mô hình đầy đủ

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

Đó là ** NOT ** Object Oriented. Lớp cơ sở không nên biết về các lớp dẫn xuất của nó. – gdoron

+1

Thật khó để hình dung những gì bạn đã làm. Vui lòng thêm mã mà bạn đã viết cho câu hỏi của mình. – Abbas

Trả lời

1

Bạn có thể sử dụng một từ điển từ kiểu enum để một hàm. Các chức năng tạo đối tượng chiến lược của bạn:

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

từ điển này được sử dụng để tạo các đối tượng của bạn dựa trên các giá trị enum:

var newStategy = strategyFactories[myEnumValue](); 

nhà máy chức năng cần phải được thêm vào từ điển bằng cách nào đó. Đối với điều đó bạn có thể tiếp xúc với đăng ký (và có thể unregister) phương pháp.

0

Bạn có thể tạo một thuộc tính mà sẽ đưa Type rằng giá trị enum đại diện và áp dụng nó vào lĩnh vực enum như vậy:

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

Phần còn lại tùy thuộc vào bạn là làm thế nào bạn muốn tạo một thể hiện của lớp của bạn. Nếu tất cả chúng đều có cùng một hàm tạo, thì phương thức này sẽ dễ dàng như bạn có thể gọi Type.GetConstructor(Type[]) và sau đó gọi Invoke ví dụ ConstructorInfo của bạn hoặc bạn có thể sử dụng một thùng chứa IoC và giải quyết một cá thể từ loại đó, chứ không phải lo lắng quá nhiều về các nhà xây dựng. thông số.

Sau đó, bạn có thể tạo ra một lớp tĩnh cho phương pháp khuyến nông trên enum của bạ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 */); 
    } 
} 

Cuối cùng, bạn sẽ gọi nó là như vậy:

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

này nên giữ cho bạn khỏi vi phạm mở/đóng nguyên tắc và cho phép bạn thêm nhiều lớp như bạn muốn và chỉ thay đổi Enum có thể được tạo thành đủ chung để tạo một thể hiện của chiến lược trực tiếp từ giá trị enum.

4

Một phương pháp để đạt được đúng OCP có thể là như sau:

Xác định một phương pháp trừu tượng Is để buộc mỗi kiểu phụ cụ thể của động vật có vú để xác định xem nó có phù hợp với một giá trị nhất định của enum:

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

    public abstract bool Is(MammalTypes mammalType); 
} 

việc triển khai của Is trong lớp con sẽ như thế nào:

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

này đang được thực hiện, chúng tôi bây giờ có thể tạo ra một MammalF lớp actory rằng, khi đưa ra một động vật có vú quét giá trị enum thông qua các lớp có sẵn và khi nó tìm thấy một trận đấu, nó sẽ trả về một thể hiện của lớp đó:

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

Việc sử dụng cuối cùng sẽ trông như thế này:

var mammalFactory = new MammalFactory(); 

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

Điều này hoàn toàn tuân thủ OCP. Nó chỉ là cần thiết để tạo ra một lớp Mammal mới cho nó được tự động có dây và sẵn sàng để sử dụng trong ứng dụng. (Không cần phải sửa đổi bất cứ điều gì khác trong việc áp dụng, trừ các enum chính nó)

Có một số vấn đề với cách tiếp cận này:

  • nó chỉ quét lắp ráp hiện đang thực hiện với nhiều loại động vật có vú
  • nó có để tạo ra một thể hiện của động vật có vú mỗi khi nó cần phải kiểm tra xem loại đó là thích hợp

trong khi những vấn đề này có thể được giải quyết, người ta vẫn còn để lại: phức tạp.

Đây là phức tạp bởi vì:

  • chúng tôi đã tăng gấp đôi số lượng mã cần thiết
  • tự động hệ thống dây điện có thể gây nhầm lẫn cho người mới đến dự án

Tôi nghĩ rằng kết luận là điều này: các mẫu thiết kế không phải là các quy tắc nghiêm ngặt. Nó không phải là giá trị làm một cái gì đó chỉ để phù hợp với một thiết kế nhất định. Thay vào đó, chúng ta phải thực dụng và thấy rằng sự cân bằng hoàn hảo giữa sự phù hợp với mô hình và tính hữu ích/đơn giản/dễ đọc. Điều này phụ thuộc rất nhiều vào vấn đề chúng tôi cố gắng giải quyết và trong nhiều trường hợp, nó có thể rất tốt là tuyên bố switch mà bạn trình bày trong câu hỏi.

Các vấn đề liên quan