现在我正在尝试使用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完全错误的方式? 你会如何解决这个问题?
答案 0 :(得分:24)
您的控制容器倒置不是工厂本身。您的案例非常适合工厂模式。
创建一个新的抽象工厂,用于创建怪物:
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容器并将工厂和怪物紧密地连接在一起。还是我又走错了路? ;-)
工厂与怪物实施紧密结合并不重要。因为这是工厂的目的:抽象出对象创建,以便代码中没有任何其他东西能够识别混凝土。
你可以创建SuperDeluxeMonsterFactory
,MonstersForCheapNonPayingUsersFactory
等。你的应用程序中的所有其他代码都不会意识到你正在使用不同的怪物(通过使用不同的工厂)。
每次必须更换混凝土时,您可以切换工厂,也可以只修改现有工厂。只要您的怪物实施不违反Liskovs替代原则,其他任何代码都不会受到影响。
那么工厂和IoC容器之间有什么区别呢? IoC非常适合解析类的依赖关系并维护生命周期(容器可以在HTTP请求结束时自动处理所有一次性用法)..
另一方面,工厂擅长为您创建物体。它做到了,没有别的。
摘要
因此,如果您的代码中某处需要获取特定类型的实现,则通常应使用工厂。工厂本身可以在内部使用IoC作为服务定位器(以解决依赖关系)。这没关系,因为它是工厂中的实现细节,不会影响应用程序中的任何其他内容。
如果要解析服务,请使用IoC容器(通过依赖注入)(并且不关心您获得的实现,或者您是否获得了先前创建的实例)。