一个接口与 DI 的多种实现 [英] Multiple implementations for one interface with DI

查看:23
本文介绍了一个接口与 DI 的多种实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

现在我正在尝试使用 Autofac 的 IOC 容器自学依赖注入模式.我想出了一个非常简单的例子,如下所示.虽然这个例子很简单,但我没有让它正常工作.

Right now I'm trying to teach myself the Dependency Injection pattern with the IOC-container from Autofac. I've come up with a very simple example, which is presented below. Although the example is simple, I fail to get it working properly.

这是我的类/接口:

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

Two monsters, both implementing the IMonster interface:

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<int, IMonster>    mVampireFactory;
  Func<string, IMonster> mZombieFactory;

  public Graveyard(Func<int, IMonster> vampireFactory, Func<string, IMonster> 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<Graveyard>().As<ILocation>();
  builder.RegisterType<Vampire>().As<IMonster>();
  builder.RegisterType<Zombie>().As<IMonster>();
  var container = builder.Build();

  // It's midnight!
  var location = container.Resolve<ILocation>();
  location.PresentLocalCreeps();

  // Waiting for dawn to break...
  Console.ReadLine(); 
  container.Dispose();
}

这是我的问题:在运行时,Autofac 在这一行抛出异常:

And this is my problem: During runtime, Autofac throws an exception on this line:

var vampire = mVampireFactory.Invoke(300);

看来mVampireFactory实际上是在尝试实例化一个僵尸.当然这行不通,因为僵尸的构造函数不会接受 int.

It seems that the mVampireFactory is actually trying to instantiate a zombie. Of course this won't work since the zombie's constructor won't take an int.

有没有简单的方法来解决这个问题?或者我是否理解 Autofac 的工作方式完全错误?你会如何解决这个问题?

Is there a simple way to fix this? Or did I get the way Autofac works completely wrong? How would you solve this problem?

推荐答案

您的控制反转容器本身并不是工厂.您的机箱非常适合工厂模式.

Your inversion of control container is not a factory per se. Your case is a perfect fit for the factory pattern.

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

Create a new abstract factory which is used to create your monsters:

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

然后在 Autofac 中注册它的实现.

And then register its implementation in Autofac.

最后在你的类中使用工厂:

Finally use the factory in your 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();
  }
}

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

You can of course use specific monster factories too if you want. None the less, using interfaces will imho make your code a lot more readable.

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

But how would I implement the factory? On the one hand the factory should not use the IOC container to create the monsters, because that's considered evil (degrades the DI pattern to the service locator anti-pattern).

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

I'm getting so tired of hearing that SL is an anti-pattern. It's not. As with all patterns, if you use it incorrectly it will give you a disadvantage. That applies for ALL patterns. http://blog.gauffin.org/2012/09/service-locator-is-not-an-anti-pattern/

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

But in this case I don't see why you can't create the implementations directly in your factory? That's what the factory is for:

public class PreferZombiesMonsterFactory : IMonsterFactory
{
    public Zombie CreateZombie(string name)
    {
        return new SuperAwesomeZombie(name);
    }

    public Vampire CreateVampire(int age)
    {
        return new BooringVampire(age);
    }
}

没有比这更复杂的了.

另一方面,工厂不应该自己创建怪物,因为这会绕过 IOC 容器并将工厂和怪物紧密耦合.还是我又走错了路?;-)

On the other hand the factory should not create the monsters itself, because that would bypass the IOC-container and tightly couple the factory and the monsters. Or am I on the wrong track again? ;-)

工厂与怪物实现紧密耦合并不重要.因为这就是工厂的目的:抽象出对象的创建,这样你的代码中的其他任何东西都不知道具体的东西.

It doesn't matter that the factory is tighly coupled to the monster implementations. Because that's the purpose of the factory: To abstract away the object creation, so that nothing else in your code is aware of the concretes.

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

You could create SuperDeluxeMonsterFactory, MonstersForCheapNonPayingUsersFactory etc. All other code in your application wouldn't be aware of that you are using different monsters (by using different factories).

每次您必须更改混凝土时,您要么切换工厂,要么修改现有工厂.只要您的怪物实现不违反 Liskovs 替换原则,其他代码就不会受到影响.

Each time you have to change concretes you either switch factory or you just modify the existing factory. No other code will be affected as long as your monster implementations do not violate Liskovs Substitution Principle.

那么工厂和 IoC 容器之间有什么区别呢?IoC 非常擅长解决类的依赖关系并维护生命周期(例如,当 HTTP 请求结束时,容器可以自动处理所有一次性物品).

So what's the difference between a factory and a IoC container then? The IoC is great at resolving dependencies for your classes and maintain the lifetimes (the container can for instance dispose all disposables automatically when a HTTP request ends)..

另一方面,工厂擅长为您创建对象.它做到了这一点,没有别的.

The factory on the other hand excels at creating objects for you. It does that and nothing else.

总结

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

So if you somewhere in your code need to get a specific type of an implementation you typically should use a factory. The factory itself CAN use the IoC as a service locator internally (to resolve dependencies). That is OK since it's a implementation detail in the factory which do not affect anything else in your application.

如果你想解析一个服务(并且不关心你得到哪个实现,或者你是否得到一个以前创建的实例),请使用 IoC 容器(通过依赖注入).

Use the IoC container (through dependency injection) if you want to resolve a service (and do not care which implementation you get, or if you get a previously created instance).

这篇关于一个接口与 DI 的多种实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆