我已经使用依赖注入(DI)有一段时间了,在构造函数、属性或方法中进行注入。我从未觉得有必要使用反转控制(IoC)容器。然而,我读得越多,我就越感到来自社区的使用IoC容器的压力。

我使用过StructureMap、NInject、Unity和Funq等。net容器。我仍然没有看到IoC容器将如何受益/改进我的代码。

我也害怕在工作中开始使用容器,因为我的许多同事会看到他们不理解的代码。他们中的许多人可能不愿意学习新技术。

请说服我,我需要使用IoC容器。当我在工作中与其他开发人员交谈时,我将使用这些论点。


当前回答

我知道这是一个相当老的帖子,但它似乎仍然相当活跃,我想我可以贡献一些在其他回答中没有提到的观点。

我同意依赖注入的好处,但我更喜欢自己构造和管理对象,使用与Maxm007在回答中概述的模式相似的模式。我发现了使用第三方容器的两个主要问题:

1) Having a 3rd party library manage the lifetime of your objects "automagically" can lend itself to unexpected results. We have found that especially in large projects, you can have vastly more copies of an object than you expect, and more than you would if you were manually managing the lifecycles. I'm sure this varies depending on the framework used, but the problem exists nonetheless. This can also be problematic if your object holds resources, data connections, etc., since the object can sometimes live longer than you expect. So inevitably, IoC containers tend to increase the resource utilization and memory footprint of an application.

2) IoC containers, in my opinion, are a form of "black box programming". I have found that in particular, our less experienced developers tend to abuse them. It allows the programmer to not have to think about how objects should relate to each other or how to decouple them, because it provides them with a mechanism in which they can simply grab any object they want out of thin air. Eg, there may be a good design reason that ObjectA should never know about ObjectB directly, but rather than creating a factory or bridge or service locator, an inexperienced programmer will simply say "no problem, I'll just grab ObjectB from the IoC container". This can actually lead to increased object coupling, which is what IoC is supposed to help prevent.

其他回答

在。net世界里AOP还不是很流行,所以对于依赖注入来说,一个框架是你唯一真正的选择,不管你是自己写一个框架还是使用另一个框架。

如果您使用AOP,则可以在编译应用程序时进行注入,这在Java中更为常见。

DI有很多好处,比如减少耦合,从而使单元测试更容易,但是您将如何实现它呢?你想用反射来自己做吗?

哇,真不敢相信乔尔会喜欢这个

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

在这:

var svc = IoC.Resolve<IShippingService>();

许多人没有意识到你的依赖链可能会变成嵌套的,手动连接它们很快就会变得笨拙。即使使用工厂,重复代码也是不值得的。

IoC容器可以很复杂,是的。但是对于这个简单的例子,我已经证明了它非常简单。


好吧,让我们进一步证明这一点。假设您有一些想要绑定到智能UI的实体或模型对象。这个智能UI(我们称它为Shindows Morms)希望你实现INotifyPropertyChanged,这样它就可以进行更改跟踪并相应地更新UI。

"好吧,听起来没那么难"于是你开始写。

你可以这样开始:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

..结果是这样的:

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

这是令人作呕的管道代码,我认为如果你手写这样的代码,你就是在从客户那里偷东西。有更好更聪明的工作方式。

听过“更聪明地工作,而不是更努力地工作”这句话吗?

想象一下,你团队里有个聪明人说:“这有个更简单的方法”

如果你让你的财产是虚拟的(冷静点,这没什么大不了的),那么我们就可以自动地编织财产行为。(这被称为AOP,但是不要担心它的名字,专注于它将为你做什么)

根据你使用的IoC工具,你可以这样做:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

噗!所有手动INotifyPropertyChanged BS现在都自动为您生成,在对象的每个虚拟属性setter上。

这是魔法吗?是的!如果您相信这段代码完成了它的工作,那么您就可以安全地跳过所有属性包装的繁文缛节。你有业务问题要解决。

IoC工具用于AOP的其他一些有趣的用法:

声明式和嵌套的数据库事务 声明性和嵌套的工作单元 日志记录 前/后条件(按合同设计)

想必没有人强迫您使用依赖注入容器框架。您已经使用DI来解耦类并改进可测试性,因此您将获得许多好处。简而言之,你喜欢简单,这通常是件好事。

如果您的系统达到了一个复杂的水平,手动DI变成了一件苦差事(也就是说,增加了维护),请将其与DI容器框架的团队学习曲线进行权衡。

如果您需要对依赖生命周期管理进行更多的控制(也就是说,如果您觉得需要实现单例模式),请查看DI容器。

如果使用DI容器,请只使用需要的特性。跳过XML配置文件,在代码中配置即可。坚持构造函数注入。Unity或StructureMap的基本内容可以压缩到几个页面中。

Mark Seemann写了一篇很棒的博文:什么时候使用DI容器

你不需要一个框架来实现依赖注入。您也可以通过java核心概念来实现这一点。 http://en.wikipedia.org/wiki/Dependency_injection#Code_illustration_using_Java

无论何时使用“new”关键字,您都是在创建一个具体的类依赖,您的头脑中应该敲响警钟。孤立地测试这个物体变得更加困难。解决方案是对接口进行编程并注入依赖项,这样对象就可以用实现该接口的任何东西进行单元测试。模拟)。

The trouble is you have to construct objects somewhere. A Factory pattern is one way to shift the coupling out of your POXOs (Plain Old "insert your OO language here" Objects). If you and your co-workers are all writing code like this then an IoC container is the next "Incremental Improvement" you can make to your codebase. It'll shift all that nasty Factory boilerplate code out of your clean objects and business logic. They'll get it and love it. Heck, give a company talk on why you love it and get everyone enthused.

如果你的同事还没有做DI,那么我建议你先专注于DI。宣传如何编写易于测试的干净代码。干净的DI代码是比较困难的部分,一旦做到了这一点,将对象连接逻辑从Factory类转移到IoC容器应该是相对简单的。