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

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

问题描述

现在我正在使用Autofac的IOC容器自我介绍Dependency Injection模式。我已经提出了一个非常简单的例子,如下所示。虽然这个例子很简单,但是我无法正常工作。



这是我的课程/界面:



两个执行IMonster界面的怪物:

 界面IMonster 
{
void IntroduceYourself();
}

class吸血鬼:IMonster
{
public delegate吸血鬼工厂(int age);

int mAge;

public Vampire(int age)
{
mAge = age;
}

public void IntroduceYourself()
{
Console.WriteLine(我是一个+ mAge +岁的吸血鬼!
}
}

class Zombie:IMonster
{
公共委托僵尸工厂(字符串名称);

string mName;

public Zombie(string name)
{
mName = name;
}

public void IntroduceYourself()
{
Console.WriteLine(我是+ mName +僵尸!
}
}

然后是我的墓地:

 接口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)
{
//安装Autofac
var builder = new ContainerBuilder );
builder.RegisterType&Graceyard>()。As< ILocation>();
builder.RegisterType< Vampire>()。As< IMonster>();
builder.RegisterType< Zombie>()。As< IMonster>();
var container = builder.Build();

//是午夜!
var location = container.Resolve< ILocation>();
location.PresentLocalCreeps();

//等待黎明破解
Console.ReadLine();
container.Dispose();
}

这是我的问题:
在运行时,Autofac会抛出一个这行上的例外:

  var vampire = mVampireFactory.Invoke(300); 

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



有一个简单的方法来解决这个问题吗?
或者我得到Autofac的工作方式完全错了吗?
您将如何解决这个问题?

解决方案

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



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

  public interface IMonsterFactory 
{
Zombie CreateZombie(string name);
吸血鬼CreateVampire(int age);
}

然后在Autofac中注册其实现。



最后在你的班上使用工厂:

  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 name)
{
返回新的SuperAwesomeZombie(name);
}

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

这不比这更复杂。


另一方面,工厂不应该自己创建怪物,因为这将绕过IOC容器并紧紧耦合工厂和怪物。还是我在错误的轨道上呢? ;-)


这个工厂与怪物实现紧密联系并不重要。因为这是工厂的目的:要抽象出对象的创建,所以你的代码中没有其他内容可以知道这个混合。



你可以创建 SuperDeluxeMonsterFactory MonstersForCheapNonPayingUsersFactory 等你应用程序中的所有其他代码都不会知道你使用的是不同的怪物(通过使用不同的工厂)



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



工厂与IoC容器



那么工厂和IoC容器之间有什么区别呢? IoC非常适合解决您的课程的依赖关系并维持生命周期(容器可以例如在HTTP请求结束时自动处理所有一次性物品)。



工厂另一方面擅长为您创建对象。这样做,没有别的。



摘要



所以如果你的代码中的某个地方需要具体通常应该使用工厂的实现类型。工厂本身可以在内部使用IoC作为服务定位器(以解决依赖关系)。这是可以的,因为它是工厂中的一个实现细节,不影响你应用程序中的任何其他东西。



如果要解决,请使用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.

Here are my classes/interfaces:

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

Then there's my graveyard:

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

And finally my main:

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

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

var vampire = mVampireFactory.Invoke(300);

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.

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

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.

Update

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).

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

It's not more complicated than that.

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.

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).

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.

Factory vs IoC container

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.

Summary

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.

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天全站免登陆