2016-11-23 93 views
2

我正在阅读这篇文章,以删除C#中的开关案例,并使用多态性来实现它。Polymorphim:如何创建接口的动态实例

https://refactoring.guru/replace-conditional-with-polymorphism

这里是我的代码,但:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace Consolas 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      var animalType = "Dog"; //consider this as an argument supplied to the app 
      //How to create dynamic instance for IAnimal ??? 
      //Is Activator.CreateInstance the only way to achieve it ?? 
      //IAnimal animal = ..what here ? 
      //animal.Bark(); 
     } 
    } 

    public interface IAnimal 
    { 
     void Bark(); 
    } 

    public class Dog: IAnimal 
    { 
     public void Bark() 
     { 
      Console.WriteLine("Bow Bow"); 
     } 
    } 

    public class Cat: IAnimal 
    { 
     public void Bark() 
     { 
      Console.WriteLine("Meow Meow"); 
     } 
    } 
} 

我如何为接口创建实例,以便它可以动态调用该方法的树皮。

有人可以提供最好的方式,不只是一个工作。

+0

结帐[依赖注入设计模式(https://msdn.microsoft.com/en-us/library/hh323705(V = VS.100 ).aspx) – Sid

回答

5

考虑使用Factory Pattern实现这一目标,一个例子是这样的:

public class AnimalFactory 
{ 
    public IAnimal CreateAnimal(string animalType) 
    { 
     //Here you can either have a switch statement checking for 
     //type, or use Type.GetType(animalType) and then create an 
     //instance using the Activator - but in the latter case you will 
     //need to pass in the exact type name of course 

     //PS. You can also use an IoC container to resolve all 
     //implementations of IAnimal and have a distinguishing property 
     //that you use here to select the type you want, but I think 
     //that's a bit off topic so won't detail it here 
    } 
} 

static void Main(string[] args) 
{ 
     var animalType = "Dog"; 
     var amimal = new AnimalFactory().CreateAnimal(animalType); 
     animal.Bark(); 
} 

EDIT(在这个例子中AutoFac)使用IoC容器

一种方法是扫描你组装并注册所有类名为key的IAnimal实现(如果你正在注册单例实例,你可以通过接口的属性键),如下所示:

class Program 
{ 
    public static IContainer _container; 

    static void Main(string[] args) 
    { 
     //Register types 
     Register(); 

     //Resolve a dog 
     var dog = _container.ResolveKeyed<IAnimal>("Dog"); 
     //Resolve a cat 
     var cat = _container.ResolveKeyed<IAnimal>("Cat"); 

     dog.Bark(); //Bow Bow 
     cat.Bark(); //Meow Meow 

     Console.Read(); 
    } 

    static void Register() 
    { 
     //Get all types implementing IAnimal, you can of course scan multiple assemblies, 
     //here I am only looking at the current assembly 
     var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => !t.IsInterface && t.IsAssignableTo<IAnimal>()); 

     var builder = new ContainerBuilder(); 
     foreach (var t in types) 
     { 
      //Use the type name as a key to the instance 
      builder.RegisterType(t).Keyed<IAnimal>(t.Name) 
       .InstancePerDependency(); //You want a new instance each time you resolve 
     }   

     _container = builder.Build();    
    } 
} 

当然,您仍然可以将其重构为工厂 PS。使用AutoFac的Assembly Scanning可能会有更好的方法来注册程序集中的所有类型,但是我看不出有什么方法可以将它与每种类型的键结合使用。

+1

+1,只是要注意 - 在这种情况下使用开关绝对没有错。我更喜欢通过Activator方法。 –

+0

@AntP只要实现的数量有限且类在编译时已知,则为true;国际海事组织最好的选择是通过一个IoC容器来解决,但对于问题的范围看起来有点矫枉过正 – KMoussa

+0

@AntP动物可以在某个时间后增长。因此,如果我使用开关,如果我将其写入Main()方法本身,而不是CreateAnimal方法 –

1

在最简单的情况下(只对应stringIAnimal接口的实现),可以使用字典

private static Dictionary<String, Func<IAnimal>> s_Factory = 
    new Dictionary<string, Func<IAnimal>>(StringComparer.OrdinalIgnoreCase) { 

    {"dog",() => new Dog()}, 
    {"hound",() => new Dog()}, // synonym to dog 
    {"cat",() => new Cat()}  
    }; 

所以你把

static void Main(string[] args) 
    { 
     var animalType = "Dog"; 

     // if we sure that animal exists we can just call the dictionary 
     IAnimal animal = s_Factory[animalType](); 

     animal.Bark(); 
    } 

或者

static void Main(string[] args) 
    { 
     Console.WriteLine("Enter animal type, please"); 
     String animalType = Console.ReadLine(); 

     Func<IAnimal> maker; 

     // If we not sure that animal exists, we have to check 
     if (s_Factory.TryGetValue(animalType, out maker)) 
     maker().Bark(); 
     else 
     Console.WriteLine("Sorry, I don't know such an animal"); 
    } 

一般情况下,然而,你必须实现工厂模式(见KMoussa的答案)

0

对于这类问题,通常我用Activator.CreateInstance()方法,但作为KMoussa说,使用一个工厂来处理的创建的IAnimal的具体类是一个很好的(和推荐)的方式。这是我的一个项目的例子,它有点类似。

public class ItemFactory 
{ 
    /* 
    * Singleton pattern for factory, cause generally a factory only has one instance through out the app. 
    */ 
    private static ItemFactory itemFactory; 
    public static ItemFactory Instance 
    { 
     get 
     { 
      if (itemFactory == null) 
       itemFactory = new ItemFactory(); 
      return itemFactory; 
     } 
    } 

    // method to create an instance of IItem, note the static modifier if you want to call it from base class, if 
    // you use singleton, ignore static. 
    public /*static*/ IItem CreateItem(string itemType) 
    { 
     Type type = Type.GetType(itemType); 
     var temp = Activator.CreateInstance(type); 
     return temp; 
    } 
} 

以后你可以,

var a = ItemFactory.Instance.CreateItem("RawItem"); 
a.RawItemMethod1(); 
a.RawItemMethod2();