我已经使用依赖注入(DI)有一段时间了,在构造函数、属性或方法中进行注入。我从未觉得有必要使用反转控制(IoC)容器。然而,我读得越多,我就越感到来自社区的使用IoC容器的压力。
我使用过StructureMap、NInject、Unity和Funq等。net容器。我仍然没有看到IoC容器将如何受益/改进我的代码。
我也害怕在工作中开始使用容器,因为我的许多同事会看到他们不理解的代码。他们中的许多人可能不愿意学习新技术。
请说服我,我需要使用IoC容器。当我在工作中与其他开发人员交谈时,我将使用这些论点。
IoC框架非常棒,如果你想…
…扔掉类型安全。许多(?)IoC框架迫使您执行代码,如果您想确保所有东西都正确连接。“嘿!希望我已经把一切都设置好了,这样我对这100个类的初始化就不会在生产中失败,抛出空指针异常!”
...在代码中使用全局变量(IoC框架都是关于改变全局状态的)。
...编写依赖关系不明确、难以重构的蹩脚代码,因为你永远不知道什么依赖什么。
IoC的问题在于,使用它的人过去常常编写这样的代码
public class Foo {
public Bar Apa {get;set;}
Foo() {
Apa = new Bar();
}
}
这显然是有缺陷的,因为Foo和Bar之间的依赖是硬连接的。然后他们意识到编写这样的代码会更好
public class Foo {
public IBar Apa {get;set;}
Foo() {
Apa = IoC<IBar>();
}
}
这也有缺陷,但不那么明显。
在Haskell中,Foo()的类型将是IO Foo,但你真的不想要IO部分,这应该是一个警告信号,如果你得到了它,你的设计有问题。
为了摆脱它(io部分),获得ioc框架的所有优点,去掉它的所有缺点,你可以使用抽象工厂。
正确的解决方法应该是
data Foo = Foo { apa :: Bar }
或者
data Foo = forall b. (IBar b) => Foo { apa :: b }
和inject(但我不会叫它inject) Bar。
另外:观看Erik Meijer (LINQ的发明者)的视频,他说DI是为不懂数学的人准备的(我非常同意):http://www.youtube.com/watch?v=8Mttjyf-8P4
不像Spolsky先生,我不相信使用ioc框架的人很聪明——我只是认为他们不懂数学。
IoC容器也适用于装入嵌套很深的类依赖项。例如,如果你有以下代码使用依赖注入。
public void GetPresenter()
{
var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}
class CustomerPresenter
{
private readonly ICustomerService service;
public CustomerPresenter(ICustomerService service)
{
this.service = service;
}
}
class CustomerService
{
private readonly IRespository<Customer> repository;
public CustomerService(IRespository<Customer> repository)
{
this.repository = repository;
}
}
class CustomerRepository : IRespository<Customer>
{
private readonly DB db;
public CustomerRepository(DB db)
{
this.db = db;
}
}
class DB { }
如果你把所有这些依赖都加载到一个IoC容器中,你可以解析CustomerService,所有的子依赖都会自动得到解析。
例如:
public static IoC
{
private IUnityContainer _container;
static IoC()
{
InitializeIoC();
}
static void InitializeIoC()
{
_container = new UnityContainer();
_container.RegisterType<ICustomerService, CustomerService>();
_container.RegisterType<IRepository<Customer>, CustomerRepository>();
}
static T Resolve<T>()
{
return _container.Resolve<T>();
}
}
public void GetPresenter()
{
var presenter = IoC.Resolve<CustomerPresenter>();
// presenter is loaded and all of its nested child dependencies
// are automatically injected
// -
// Also, note that only the Interfaces need to be registered
// the concrete types like DB and CustomerPresenter will automatically
// resolve.
}