与DI一个接口的多种实现(Multiple implementations for one inte

2019-08-21 17:21发布

现在,我想教自己与Autofac的IOC容器的依赖注入模式。 我想出了一个非常简单的例子,下面介绍。 虽然例子很简单,我无法得到它的正常工作。

这里是我的类/接口:

两个怪物,既实现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<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抛出在这条线的异常:

var vampire = mVampireFactory.Invoke(300);

看来,mVampireFactory实际上是试图实例化一个僵尸。 当然,这是行不通的,因为僵尸的构造不会采取一个int。

有没有一种简单的方法来解决这一问题? 还是因为我得到Autofac工作的方式完全错了吗? 你将如何解决这个问题?

Answer 1:

你控制容器的反转是不是有厂家本身。 你的情况是工厂模式完美契合。

创建一个新的抽象工厂是用来创建您的怪物:

public interface IMonsterFactory
{
    Zombie CreateZombie(string name);
    Vampire 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(string name)
    {
        return new SuperAwesomeZombie(name);
    }

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

这不是要复杂得多。

在另一方面,工厂不应该创建怪物本身,因为这将绕过IOC容器和紧密结合工厂和怪物。 再或者是我在错误的轨道上? ;-)

这不要紧,工厂tighly耦合到怪物的实现。 因为这是工厂的宗旨:抽象掉的对象的创建,使闲来无事在你的代码是意识到了混凝土。

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

每次有时间来改变混凝土你要么开关厂,或者你只是修改现有的工厂。 没有任何其他代码都将受到影响,只要你的怪物实现不违反Liskovs替代原则。

工厂VS IoC容器

那么什么是工厂和IoC容器,然后有什么区别? 国际奥委会在解决您的类的依赖性很大,维持寿命(容器例如可以自动处理所有耗材当HTTP请求结束)..

在另一方面工厂擅长为你创建对象。 它说,没有别的。

摘要

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

如果要解决服务使用IoC容器(通过依赖注入)(并不在乎你得到它的实现,或者如果你以前创建的实例)。



文章来源: Multiple implementations for one interface with DI