DI的一个接口的多个实现

现在我正在尝试使用Autofac的IOC容器自学习dependency injection模式。 我想出了一个非常简单的例子,如下所示。 虽然示例很简单,但我无法正常工作。

这是我的类/接口:

两个怪物,都实现了IMonster接口:

interface IMonster { void IntroduceYourself(); } class Vampire : IMonster { public delegate Vampire Factory(int age); int mAge; public Vampire(int age) { mAge = age; } public void IntroduceYourself() { Console.WriteLine("Hi, I'm a " + mAge + " years old vampire!"); } } class Zombie : IMonster { public delegate Zombie Factory(string name); string mName; public Zombie(string name) { mName = name; } public void IntroduceYourself() { Console.WriteLine("Hi, I'm " + mName + " the zombie!"); } } 

然后是我的墓地:

 interface ILocation { void PresentLocalCreeps(); } class Graveyard : ILocation { Func mVampireFactory; Func mZombieFactory; public Graveyard(Func vampireFactory, Func zombieFactory) { mVampireFactory = vampireFactory; mZombieFactory = zombieFactory; } public void PresentLocalCreeps() { var vampire = mVampireFactory.Invoke(300); vampire.IntroduceYourself(); var zombie = mZombieFactory.Invoke("Rob"); zombie.IntroduceYourself(); } } 

最后我的主要:

 static void Main(string[] args) { // Setup Autofac var builder = new ContainerBuilder(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); var container = builder.Build(); // It's midnight! var location = container.Resolve(); location.PresentLocalCreeps(); // Waiting for dawn to break... Console.ReadLine(); container.Dispose(); } 

这是我的问题:在运行时,Autofac会在此行引发exception:

 var vampire = mVampireFactory.Invoke(300); 

似乎mVampireFactory实际上试图实例化一个僵尸。 当然这不起作用,因为僵尸的构造函数不会采用int。

有没有一种简单的方法来解决这个问题? 或者我是否得到了Autofac完全错误的方式? 你怎么解决这个问题?

您的控制容器倒置不是工厂本身。 您的案例非常适合工厂模式。

创建一个用于创建怪物的新抽象工厂:

 public interface IMonsterFactory { Zombie CreateZombie(string name); Vampire CreateVampire(int age); } 

然后在Autofac中注册其实现。

最后在你class上使用工厂:

 class Graveyard : ILocation { IMonsterFactory _monsterFactory; public Graveyard(IMonsterFactory factory) { _monsterFactory = factory; } public void PresentLocalCreeps() { var vampire = _monsterFactory.CreateVampire(300); vampire.IntroduceYourself(); var zombie = _monsterFactory.CreateZombie("Rob"); zombie.IntroduceYourself(); } } 

如果你愿意,你当然也可以使用特定的怪物工厂。 尽管如此,使用接口将使您的代码更具可读性。

更新

但是我该如何实施工厂呢? 一方面,工厂不应该使用IOC容器来创建怪物,因为这被认为是邪恶的(将DI模式降级为服务定位器反模式)。

我已经厌倦了听说SL是一种反模式。 不是。 与所有模式一样,如果你使用不正确,它会给你一个劣势。 这适用于所有模式。 http://blog.gauffin.org/2012/09/service-locator-is-not-an-anti-pattern/

但在这种情况下,我不明白为什么你不能直接在你的工厂创建实现? 这就是工厂的用途:

 public class PreferZombiesMonsterFactory : IMonsterFactory { public Zombie CreateZombie(string name) { return new SuperAwesomeZombie(name); } public Vampire CreateVampire(int age) { return new BooringVampire(age); } } 

它并不复杂。

另一方面,工厂本身不应该制造怪物,因为它会绕过IOC容器并将工厂和怪物紧密地连接在一起。 还是我又走错了路? 😉

工厂与怪物实现紧密耦合并不重要。 因为这是工厂的目的:抽象出对象创建,以便代码中没有任何其他东西能够识别混凝土。

您可以创建SuperDeluxeMonsterFactoryMonstersForCheapNonPayingUsersFactory等。您的应用程序中的所有其他代码都不会意识到您正在使用不同的怪物(通过使用不同的工厂)。

每次必须更换混凝土时,您可以切换工厂,也可以只修改现有工厂。 只要您的怪物实施不违反Liskovs替代原则,任何其他代码都不会受到影响。

Factory vs IoC容器

那么工厂和IoC容器之间有什么区别呢? IoC非常适合解析类的依赖关系并维护生命周期(容器可以在HTTP请求结束时自动处理所有一次性用法)。

另一方面,工厂擅长为您创建物体。 它做到了,没有别的。

摘要

因此,如果您的代码中某处需要获取特定类型的实现,则通常应使用工厂。 工厂本身可以在内部使用IoC作为服务定位器(以解决依赖关系)。 这没关系,因为它是工厂中的实现细节,不会影响应用程序中的任何其他内容。

如果要解析服务,请使用IoC容器(通过dependency injection)(并且不关心您获得的实现,或者是否获得先前创建的实例)。