首页 文章

为什么我需要一个IoC容器而不是直接的DI代码? [关闭]

提问于
浏览
598

我一直在使用Dependency Injection(DI)一段时间,在构造函数,属性或方法中注入 . 我从未觉得需要使用Inversion of Control(IoC)容器 . 但是,我读的越多,我觉得社区使用IoC容器的压力就越大 .

我使用.NET容器,如StructureMapNInjectUnityFunq . 我仍然没有看到IoC容器如何使我的代码受益/改进 .

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

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

30 回答

  • 0

    关于Unity的Dittos . 太大了,你可以听到椽子里的吱吱声 .

    当人们开始大肆宣传IoC代码看起来如何干净时,我从来没有感到惊讶的是同样的人们曾经谈过C中的模板是如何在90年代回归的优雅方式,但现在却将它们谴责为奥术 . 呸!

  • 1

    在.NET世界中,AOP并不太受欢迎,因此对于DI来说,框架是您唯一真正的选择,无论您是自己编写还是使用其他框架 .

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

    DI有许多好处,例如减少耦合,因此单元测试更容易,但您将如何实现它?你想用反射来自己做吗?

  • 16

    哇,简直不敢相信乔尔会赞成这个:

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

    ..最终得到 this

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

    那个's disgusting plumbing code, and I maintain that if you'用手写的代码 you're stealing from your client . 有更好,更聪明的工作方式 .

    曾经听过这个词,工作更聪明,而不是更难?

    想象一下,你团队中的一些聪明人出现说:“这是一种更简单的方法”

    如果你让你的属性变得虚拟(冷静下来,这不是什么大不了的事),那么我们就可以自动编织这个属性行为了 . (这叫做AOP,但不要为你做什么)

    根据您使用的IoC工具,您可以执行以下操作:

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

    Poof! 所有手册INotifyPropertyChanged BS现在都是在相关对象的每个虚拟属性设置器上自动生成的 .

    这太神奇了吗? YES !如果您可以相信这段代码能够完成它的工作,那么您可以安全地跳过所有包含mumbo-jumbo的属性 . 你有业务问题需要解决 .

    使用IoC工具进行AOP的其他一些有趣的用途:

    • 声明性和嵌套数据库事务

    • 声明和嵌套工作单元

    • 记录

    • 前/后条件(按 Contract 设计)

  • 23

    我和你在一起,瓦迪姆 . IoC容器采用简单,优雅和有用的概念,并使用200页的手册使您需要学习两天 .

    我个人很困惑IoC社区如何把它变成了一个漂亮的,并且把它变成了一堆复杂的框架,通常有200-300页的手册 .

    我尽量不做评判(哈哈!),但我认为使用IoC容器的人是(A)非常聪明,而且(B)缺乏对那些不是't as smart as they are. Everything makes perfect sense to them, so they have trouble understanding that many ordinary programmers will find the concepts confusing. It'的人的同情 . 了解IoC容器的人很难相信有些人不理解它 .

    使用IoC容器最有 Value 的好处是,您可以在一个位置使用配置开关,这样您就可以在测试模式和 生产环境 模式之间进行切换 . 例如,假设您有两个版本的数据库访问类...一个版本进行了积极记录并进行了大量验证(在开发过程中使用),另一个版本没有记录或验证,这对于 生产环境 来说非常快 . 很高兴能够在一个地方之间切换它们 . 另一方面,这是一个相当简单的问题,可以在没有IoC容器复杂性的情况下以更简单的方式轻松处理 .

    我相信如果你使用IoC容器,坦率地说,你的代码很难阅读 . 您必须查看以确定代码尝试执行的操作的位置数量至少增加一个 . 在天堂的某个地方,一个天使喊出来 .

  • 37

    据推测,没有人强迫你使用DI容器框架 . 您已经在使用DI来分离类并提高可测试性,因此您可以获得许多好处 . 简而言之,你喜欢简单,这通常是一个简单的好事 .

    如果您的系统达到复杂程度,手动DI成为一项杂务(即增加维护),请根据DI容器框架的团队学习曲线进行权衡 .

    如果您需要更多地控制依赖关系生存期管理(也就是说,如果您觉得需要实现Singleton模式),请查看DI容器 .

    如果您使用DI容器,请仅使用您需要的功能 . 如果足够,请跳过XML配置文件并在代码中对其进行配置 . 坚持构造函数注入 . Unity或StructureMap的基础知识可以缩减为几页 .

    马克·西曼(Mark Seemann)发表了一篇很棒的博客文章:When to use a DI Container

  • 1

    在我看来,IoC的首要好处是能够集中依赖项的配置 .

    如果您当前正在使用依赖注入,则代码可能如下所示

    public class CustomerPresenter
    {
      public CustomerPresenter() : this(new CustomerView(), new CustomerService())
      {}
    
      public CustomerPresenter(ICustomerView view, ICustomerService service)
      {
        // init view/service fields
      }
      // readonly view/service fields
    }
    

    如果你使用静态IoC类,而不是恕我直言,更令人困惑的配置文件,你可以有这样的东西:

    public class CustomerPresenter
    {
      public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
      {}
    
      public CustomerPresenter(ICustomerView view, ICustomerService service)
      {
        // init view/service fields
      }
      // readonly view/service fields
    }
    

    然后,你的静态IoC类看起来像这样,我在这里使用Unity .

    public static IoC
    {
       private static readonly IUnityContainer _container;
       static IoC()
       {
         InitializeIoC();
       }
    
       static void InitializeIoC()
       {
          _container = new UnityContainer();
          _container.RegisterType<ICustomerView, CustomerView>();
          _container.RegisterType<ICustomerService, CustomerService>();
          // all other RegisterTypes and RegisterInstances can go here in one file.
          // one place to change dependencies is good.
       }
    }
    
  • 5

    IoC容器也适用于加载深层嵌套的类依赖项 . 例如,如果您使用Depedency Injection获得以下代码 .

    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.
    }
    
  • 441

    我是声明性编程的粉丝(看看我回答了多少个SQL问题),但我看过的IoC容器对于他们自己的好处似乎太神秘了 .

    ...或者IoC容器的开发人员可能无法编写清晰的文档 .

    ......或者两者都适用于某种程度 .

    我不认为IoC容器的概念是坏的 . 但实现必须足够强大(即灵活),足以在各种应用程序中使用,但简单易懂 .

    它可能是六个中的六个,另外六个 . 真正的应用程序(不是玩具或演示)必然是复杂的,可以解决许多极端情况和规则异常 . 要么将这种复杂性封装在命令式代码中,要么将其封装在声明性代码中 . 但你必须在某个地方代表它 .

  • -8

    使用容器主要是从命令式/脚本式初始化和配置方式转换为声明式方式 . 这可能有一些不同的有益效果:

    • 减少hairball主程序启动例程 .

    • 启用相当深的部署时重新配置功能 .

    • 使依赖注入风格成为新工作阻力最小的路径 .

    当然,可能有困难:

    • 需要复杂的启动/关闭/生命周期管理的代码可能不容易适应容器 .

    • 您可能需要浏览任何个人,流程和团队文化问题 - 但是,这就是您问...的原因

    • 一些工具包本身正迅速变得重量级,鼓励了许多DI容器开始时的强烈依赖性作为强烈反对 .

  • 0

    听起来像 you already built your own IoC container (使用Martin Fowler描述的各种模式),并且问为什么其他人的实现比你的更好 .

    所以,你有一堆已经有效的代码 . 并且想知道为什么你想要用其他人的实现替换它 .

    Pros 用于考虑第三方IoC容器

    • 你得到免费修复的bug

    • 图书馆设计可能比你的好

    • 人们可能已经熟悉特定的图书馆

    • 图书馆可能比你的快

    • 它可能有一些你希望你实现的功能,但从来没有时间(你有一个服务定位器吗?)

    Cons

    • 你得到了免费的bug :)

    • 图书馆设计可能比你的更差

    • 您必须学习新的API

    • 您将永远不会使用太多功能

    • 调试你没写的代码通常比较困难

    • 从以前的IoC容器迁移可能很乏味

    所以,权衡你的利弊与你的利弊并做出决定 .

  • 6

    我认为通过使用DI可以获得IoC的大部分 Value . 既然你已经这样做了,剩下的好处就是增量 .

    您获得的值取决于您正在处理的应用程序类型:

    • 对于多租户,IoC容器可以处理一些用于加载不同客户端资源的基础结构代码 . 当您需要特定于客户端的组件时,请使用自定义选择器来处理逻辑,而不必担心客户端代码 . 您当然可以自己构建这个,但是here's an example了解IoC如何提供帮助 .

    • 有许多可扩展点,IoC可用于从配置加载组件 . 这是常见的构建方法,但容器提供了工具 .

    • 如果要对某些横切关注点使用AOP,则IoC provides hooks将拦截方法调用 . 这在项目上不太常见,但IoC使其更容易 .

    我以前写过这样的功能,但如果我现在需要这些功能,我宁愿使用预先构建并经过测试的工具,如果它适合我的架构 .

    如其他人所述,您还可以集中配置要使用的类 . 虽然这可能是一件好事,但它的代价是误导和复杂化 . 大多数应用程序的核心组件都没有被替换太多,因此需要进行权衡取舍 .

    我使用IoC容器并欣赏功能,但必须承认我已经注意到了权衡:我的代码在类级别变得更加清晰,在应用程序级别变得更加清晰(即可视化控制流程) .

  • 0

    那么,已经差不多3年了,嗯?

    • 50%赞成IoC框架的人不理解IoC和IoC框架之间的区别 . 我怀疑他们知道你可以在不部署到app服务器的情况下编写代码

    • 如果我们采用最流行的Java Spring框架,那么IoC配置从XMl迁移到代码中,现在看起来像这样

    `@Configuration public class AppConfig {

    public @Bean TransferService transferService() {
        return new TransferServiceImpl(accountRepository());
    }
    
    public @Bean AccountRepository accountRepository() {
        return new InMemoryAccountRepository();
    }
    

    我们需要一个框架才能完全解决这个问题?

  • 9

    我是一名康复的国际奥委会成瘾者 . 这些天我发现很难证明在大多数情况下使用IOC来证明其合理性 . IOC容器牺牲了编译时检查,据说可以为您提供“简单”设置,复杂的生命周期管理以及在运行时动态发现依赖关系 . 我发现编译时间检查的丢失以及由此导致的运行时间魔术/异常,在绝大多数情况下都不值得花里胡哨 . 在大型企业应用程序中,他们可能很难跟踪正在发生的事情 .

    我不购买集中化论证,因为你可以非常容易地集中静态设置,为你的应用程序使用抽象工厂,并虔诚地将对象创建推迟到抽象工厂,即做适当的DI .

    为什么不这样做静态无魔法DI:

    interface IServiceA { }
    interface IServiceB { }
    class ServiceA : IServiceA { }
    class ServiceB : IServiceB { }
    
    class StubServiceA : IServiceA { }
    class StubServiceB : IServiceB { }
    
    interface IRoot { IMiddle Middle { get; set; } }
    interface IMiddle { ILeaf Leaf { get; set; } }
    interface ILeaf { }
    
    class Root : IRoot
    {
        public IMiddle Middle { get; set; }
    
        public Root(IMiddle middle)
        {
            Middle = middle;
        }
    
    }
    
    class Middle : IMiddle
    {
        public ILeaf Leaf { get; set; }
    
        public Middle(ILeaf leaf)
        {
            Leaf = leaf;
        }
    }
    
    class Leaf : ILeaf
    {
        IServiceA ServiceA { get; set; }
        IServiceB ServiceB { get; set; }
    
        public Leaf(IServiceA serviceA, IServiceB serviceB)
        {
            ServiceA = serviceA;
            ServiceB = serviceB;
        }
    }
    
    
    interface IApplicationFactory
    {
        IRoot CreateRoot();
    }
    
    abstract class ApplicationAbstractFactory : IApplicationFactory
    {
        protected abstract IServiceA ServiceA { get; }
        protected abstract IServiceB ServiceB { get; }
    
        protected IMiddle CreateMiddle()
        {
            return new Middle(CreateLeaf());
        }
    
        protected ILeaf CreateLeaf()
        {
            return new Leaf(ServiceA,ServiceB);
        }
    
    
        public IRoot CreateRoot()
        {
            return new Root(CreateMiddle());
        }
    }
    
    class ProductionApplication : ApplicationAbstractFactory
    {
        protected override IServiceA ServiceA
        {
            get { return new ServiceA(); }
        }
    
        protected override IServiceB ServiceB
        {
            get { return new ServiceB(); }
        }
    }
    
    class FunctionalTestsApplication : ApplicationAbstractFactory
    {
        protected override IServiceA ServiceA
        {
            get { return new StubServiceA(); }
        }
    
        protected override IServiceB ServiceB
        {
            get { return new StubServiceB(); }
        }
    }
    
    
    namespace ConsoleApplication5
    {
        class Program
        {
            static void Main(string[] args)
            {
                var factory = new ProductionApplication();
                var root = factory.CreateRoot();
    
            }
        }
    
        //[TestFixture]
        class FunctionalTests
        {
            //[Test]
            public void Test()
            {
                var factory = new FunctionalTestsApplication();
                var root = factory.CreateRoot();
            }
        }
    }
    

    您的容器配置是您的抽象工厂实现,您的注册是抽象成员的实现 . 如果需要新的单例依赖项,只需将另一个抽象属性添加到抽象工厂即可 . 如果您需要瞬态依赖,只需添加另一个方法并将其作为Func <>注入 .

    好处:

    • 所有设置和对象创建配置都是集中的 .

    • 配置只是代码

    • 编译时检查使您可以轻松维护,因为您不会忘记更新注册 .

    • 没有运行时反射魔法

    我建议怀疑论者给它一个下一个绿色的田野项目,并诚实地问自己,你需要哪个容器 . 稍后您可以轻松地将IOC容器考虑在内,因为您只是使用IOC容器配置模块替换工厂实现 .

  • 4

    对我来说使用IoC容器的最大好处(我个人使用Ninject)就是消除设置和其他类型的全局状态对象的传递 .

    我没有为web编程,我是一个控制台应用程序,在对象树深处的许多地方,我需要访问在对象树的完全独立的分支上创建的用户指定的设置或元数据 . 使用IoC,我只是告诉Ninject将设置视为一个单例(因为它们总是只有一个实例),请在构造函数中请求设置或字典并预先设置......当我需要它们时它们会神奇地出现!

    在不使用IoC容器的情况下,我必须将设置和/或元数据向下传递到2,3,...,n个对象,然后才能被需要它的对象使用 .

    DI / IoC容器还有许多其他好处,正如其他人在这里详细介绍的那样,从创建对象到请求对象的想法可能会令人费解,但使用DI对我和我的团队非常有帮助,所以也许你可以添加它到你的武器库!

  • 14

    如果你想要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 -part,并且应该是一个警告标志,如果你得到它,你的设计有问题 .

    要摆脱它(IO部分),获得IoC框架的所有优点,并且没有任何缺点,你可以改为使用抽象工厂 .

    正确的解决方案就是这样的

    data Foo = Foo { apa :: Bar }
    

    或者可能

    data Foo = forall b. (IBar b) => Foo { apa :: b }
    

    和注入(但我不会称之为注入)吧 .

    另外:与Erik Meijer(LINQ的发明者)一起观看此视频,他说DI适用于那些不同意的人:http://www.youtube.com/watch?v=8Mttjyf-8P4

    与Spolsky先生不同,我不相信使用IoC框架的人非常聪明 - 我只是相信他们不懂数学 .

  • 14

    我发现正确实现依赖注入往往会迫使程序员使用各种其他编程实践来帮助提高代码的可测试性,灵活性,可维护性和可伸缩性:单一责任原则,关注点分离和编码等实践反对API . 感觉就像我被迫编写更多模块化,一口大小的类和方法,这使得代码更容易阅读,因为它可以采用一口大小的块 .

    但它也倾向于创建相当大的依赖树,这些依赖树通过框架(特别是如果使用约定)比通过手工更容易管理 . 今天我想在LINQPad中快速测试一些东西,我觉得创建一个内核并加载到我的模块中太麻烦了,我最后手工编写了这个:

    var merger = new SimpleWorkflowInstanceMerger(
        new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
        new WorkflowAnswerRowUtil(
            new WorkflowFieldAnswerEntMapper(),
            new ActivityFormFieldDisplayInfoEntMapper(),
            new FieldEntMapper()),
        new AnswerRowMergeInfoRepository());
    

    回想起来,使用IoC框架本来会更快,因为模块按惯例定义了所有这些东西 .

    花了一些时间研究这个问题的答案和评论,我确信那些反对使用IoC容器的人并没有练习真正的依赖注入 . 我见过的例子是与依赖注入混淆的做法 . 有些人抱怨难以“阅读”代码 . 如果操作正确,当使用DI时,绝大多数代码应该与使用IoC容器时相同 . 差异应该完全在应用程序中的几个“启动点” .

    换句话说,如果你不喜欢IoC容器,你可能不会按照它应该的方式进行依赖注入 .

    另一点:依赖注入真的可以避免 . 例如,ASP.NET MVC尝试通过对每个请求的反射来实例化控制器 . 要手动执行依赖注入,您必须使每个控制器像这样:"context root,":

    public class MyController : Controller
    {
        private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
        public MyController()
        {
            _simpleMerger = new SimpleWorkflowInstanceMerger(
                new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
                new WorkflowAnswerRowUtil(
                    new WorkflowFieldAnswerEntMapper(),
                    new ActivityFormFieldDisplayInfoEntMapper(),
                    new FieldEntMapper()),
                new AnswerRowMergeInfoRepository())
        }
        ...
    }
    

    现在将其与允许DI框架为您进行比较:

    public MyController : Controller
    {
        private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
        public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
        {
            _simpleMerger = simpleMerger;
        }
        ...
    }
    

    使用DI框架,请注意:

    • 我可以对这个 class 进行单元测试 . 通过创建一个模拟 ISimpleWorkflowInstanceMerger ,我可以测试它是否按照我预期的方式使用,而不需要数据库连接或任何东西 .

    • 我使用的代码少得多,而且代码更容易阅读 .

    • 如果我的某个依赖项's dependency'发生了变化,我不必对控制器进行任何更改 . 当您考虑多个控制器可能使用某些相同的依赖项时,这一点尤其好 .

    • 我从未明确引用数据层中的类 . 我的Web应用程序可以只包含对包含 ISimpleWorkflowInstanceMerger 接口的项目的引用 . 这使我可以将应用程序分解为单独的模块,并维护真正的多层体系结构,从而使事情更加灵活 .

    典型的Web应用程序将拥有相当多的控制器 . 在每个控制器中手动执行DI的所有痛苦都会随着应用程序的增长而增加 . 如果您的应用程序只有一个上下文根,它从不尝试通过反射实例化服务,那么这不是一个大问题 . 然而,任何使用依赖注入的应用程序都将变得非常昂贵一旦达到一定的大小,除非你使用某种框架来管理依赖图 .

  • 2

    每当你使用“new”关键字时,你就会创建一个具体的类依赖关系,并且你的脑子里会出现一个小闹铃 . 单独测试这个对象变得更加困难 . 解决方案是编程接口并注入依赖关系,以便可以使用实现该接口的任何东西(例如,模拟)对对象进行单元测试 .

    麻烦的是你必须在某处构建对象 . 工厂模式是将耦合移出POXO的一种方法(Plain Old“在此处插入您的OO语言”对象) . 如果您和您的同事都在编写这样的代码,那么IoC容器就是您可以对代码库进行的下一个“增量改进” . 它会将所有令人讨厌的Factory样板代码移出干净的对象和业务逻辑 . 他们会得到它并喜欢它 . 哎呀,让公司谈谈你为什么喜欢它并让每个人都热情好客 .

    如果你的同事还没有做DI,那么我建议你先关注它 . 传播如何编写易于测试的干净代码 . 清洁DI代码是困难的部分,一旦你在那里,将对象布线逻辑从Factory类转移到IoC容器应该是相对微不足道的 .

  • 1

    由于所有依赖项都清晰可见,因此它可以促进创建松散耦合的组件,同时可以在整个应用程序中轻松访问和重用 .

  • 1

    您不需要IoC容器 .

    但是如果你严格遵循DI模式,你会发现有一个会删除大量冗余,无聊的代码 .

    无论如何,这通常是使用库/框架的最佳时间 - 当您了解它正在做什么并且可以在没有库的情况下完成它 .

  • 7

    我恰好正在掏出本土的DI代码并用IOC代替它 . 我可能已经删除了超过200行代码并用大约10代替它 . 是的,我必须做一些关于如何使用容器(Winsor)的知识,但我是一名从事互联网技术工作的工程师 . 21世纪所以我已经习惯了 . 我可能花了大约20分钟来看看如何 . 这非常值得我花时间 .

  • 0

    当您继续解耦类并反转依赖关系时,类继续保持较小并且“依赖关系图”的大小继续增长 . (这还不错 . )使用IoC容器的基本功能可以使所有这些对象的连接变得微不足道,但手动执行此操作会非常繁琐 . 例如,如果我想创建“Foo”的新实例但需要“Bar”,该怎么办?而“Bar”需要“A”,“B”和“C” . 而且每个人都需要其他3个等等(是的,我不能想出好的假名:)) .

    使用IoC容器为您构建对象图可以降低复杂性,并将其推送到一次性配置中 . 我只是简单地说“给我一个'Foo'”,它会找出构建一个所需要的东西 .

    有些人使用IoC容器来获得更多的基础设施,这对于高级场景来说很好,但在这些情况下我同意它可能会混淆并使代码难以阅读和调试新的开发人员 .

  • 10

    老实说,我没有找到在许多情况下需要IoC容器,而且大多数情况下,它们只会增加不必要的复杂性 .

    如果你只是为了简化构造对象而使用它,我不得不问,你是在多个位置实例化这个对象吗?单身人士不适合你的需求吗?您是否在运行时更改配置? (切换数据源类型等) .

    如果是,那么您可能需要一个IoC容器 . 如果没有,那么您只是将初始化从开发人员可以轻松看到的位置移开 .

    谁说接口比继承更好呢?说你必须使用Resharper来快速找到方法的声明 .

    我相信,对于大多数实现,说IoC Containers删除不需要的代码是一个神话 .

    首先,首先要设置容器 . 然后,您仍然需要定义需要初始化的每个对象 . 所以你不要在初始化中保存代码,你移动它(除非你的对象被多次使用 . 作为Singleton它更好吗?) . 然后,对于以这种方式初始化的每个对象,您必须创建和维护一个接口 .

    有没有人对此有任何想法?

  • 32

    如果您需要集中依赖项的配置,那么您将需要一个IoC容器,以便它们可以轻松地交换出来 . 这在TDD中最有意义,其中许多依赖项被换出,但依赖关系之间几乎没有相互依赖性 . 这样做的代价是在某种程度上混淆了对象构造的控制流程,因此具有良好组织和合理记录的配置是重要的 . 有理由这样做也很好,否则,它只是abstraction gold-plating . 我已经看到它做得很糟糕,以至于它被拖下来等同于构造函数的goto语句 .

  • 8

    Here是原因 . 该项目名为IOC-with-Ninject . 您可以使用Visual Studio下载并运行它 . 此示例使用Ninject,但所有'new'语句都在一个位置,您可以通过更改要使用的绑定模块来完全更改应用程序的运行方式 . 设置该示例,以便您可以绑定到服务的模拟版本或实际版本 . 在可能无关紧要的小项目中,但在大型项目中这是一个大问题 .

    为了清楚起见,我看到它们的优点:1)所有 new 语句位于代码根目录的一个位置 . 2)通过一次更改完全重新计算代码 . 3)'cool factor' 'cause it'的额外积分......好吧:很酷 . :p

  • 27

    我会试着从我的角度找出为什么国际奥委会可能不会有好处 .

    与其他所有内容一样,IOC容器(或爱因斯坦将其称为I = OC ^ 2)是一个您必须自己决定的概念,如果您在代码中需要或不需要它 . 最近时尚界对国际奥委会的强烈抗议只是时尚 . 不要因为时尚而堕落,这是第一次 . 您可以在代码中实现无数的概念 . 首先,我使用依赖注入,因为我已经开始编程,并在它以该名称推广时学习了这个术语 . 依赖控制是一个非常古老的主题,到目前为止,它以数万种方式得到了解决,具体取决于与什么分离的东西 . 将一切与一切分离是一种废话 . IOC容器的问题在于它试图像Entity Framework或NHibernate一样有用 . 虽然只要必须将任何数据库与系统耦合,编写对象关系映射器就是必须的,但IOC容器并不总是必需的 . 所以当IOC容器有用时:

    • 当您遇到要组织的许多依赖项的情况时

    • 当您不关心将代码与第三方产品耦合时

    • 当您的开发人员想要学习如何使用新工具时

    1:您的代码中依赖于这么多依赖项,或者您在设计早期就意识到它们并不常见 . 当抽象思维到期时,抽象思维是有用的 .

    2:将代码与第三方代码耦合是一个HuGe问题 . 我正在处理10年前的代码,当时正在关注ATL,COM,COM等高级概念 . 现在你无法使用该代码 . 我所说的是,先进的概念具有明显的优势,但从长远来看,这已取消了过时的优势本身 . 它刚刚使所有这些都变得更加昂贵 .

    3:软件开发很难 . 您可以将其扩展到无法识别的级别如果您允许某些高级概念进入您的代码 . IOC2存在问题 . 虽然它正在解耦依赖关系,但它也解耦了逻辑流程 . 想象一下,你发现了一个错误,你需要设置一个休息来检查情况 . 与任何其他先进概念一样,IOC2正在使其变得更加困难 . 修复概念中的错误比修复普通代码中的错误更困难,因为当您修复错误时,必须再次遵守概念 . (仅举一个例子,C .NET不断改变语法,以至于在重构某些旧版本的.NET之前需要仔细思考 . )那么IOC的问题是什么?问题在于解决依赖关系 . 解决的逻辑通常隐藏在IOC2本身中,可能以非常常见的方式编写,您需要学习和维护 . 您的第三方产品会在5年后出现吗?微软不是 .

    “我们知道”综合症是如何在IOC2上写的 . 这类似于自动化测试 . 乍一看,花哨的术语和完美的解决方案,您只需将所有测试执行过夜,并在早上查看结果 . 向公司解释自动化测试的真正含义是非常痛苦的 . 自动化测试绝对不是减少一夜之间可以引入的错误数量的快速方法,以提高产品质量 . 但是,时尚正在使这种观念成为令人讨厌的主导 . IOC2患有同样的综合症 . 相信您需要实施它才能使您的软件变得更好 . EvErY最近的采访我被问到我是否正在实施IOC2和自动化 . 这是时尚的标志:公司有部分代码用MFC编写,他们不会放弃 .

    你需要像软件中的任何其他概念一样学习IOC2 . 是否需要使用IOC2的决定是在团队和公司内部 . 但是,在作出决定之前,必须至少提及以上所有论点 . 只有当你看到正面胜过负面时,你才能做出积极的决定 .

    IOC2没有任何问题,只是它确实解决了它解决的问题,并引入了它引入的问题 . 没有其他的 . 然而,反对时尚是非常困难的,他们有汗口,任何事情的追随者 . 奇怪的是,当他们的幻想问题变得明显时,他们中的任何一个都没有 . 软件行业中的许多概念都得到了捍卫,因为它们创造了利润,书籍编写, Session 召开,新产品制作 . 这是时尚,通常是短暂的 . 一旦人们发现其他东西,他们就会完全抛弃它 . IOC2很有用,但它显示出与我见过的许多其他消失概念相同的符号 . 我不知道它是否会存活下来 . 没有规则 . 你认为如果它有用,它就会存活下来 . 不,它不是那样的 . 一个大的富裕公司就足够了,这个概念可以在几周内消亡 . 走着瞧 . NHibernate幸免于难,EF排名第二 . 也许IOC2也会存活下来 . 不要忘记,软件开发中的大多数概念都没有什么特别之处,它们非常合乎逻辑,简单明了,有时候记住当前的命名约定要比理解概念本身更难 . IOC2的知识是否使开发人员成为更好的开发人员?不,因为如果开发人员无法提出与IOC2性质相似的概念,那么他或她很难理解IOC2正在解决哪个问题,使用它会看起来很人为,他或她可能会开始使用它为了某种政治上的正确 .

  • 138

    就个人而言,我使用IoC作为我的应用程序的某种结构图(是的,我也更喜欢StructureMap;)) . 它使得在测试期间用Moq实现替换我的ussual接口实现变得容易 . 创建测试设置可以像对IoC框架进行新的init调用一样简单,用模拟替换我的测试边界中的哪个类 .

    这可能不是IoC的用途,但我发现自己最常使用它 .

  • 28

    国际奥委会集装箱解决了你可能没有的问题,但这是一个很好的问题

    http://kozmic.net/2012/10/23/ioc-container-solves-a-problem-you-might-not-have-but-its-a-nice-problem-to-have/

  • 32

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

  • 17

    我意识到这是一个相当古老的帖子,但它似乎仍然相当活跃,我想我会贡献一些尚未在其他方面提到的观点 . 答案 .

    我会说我同意依赖注入的好处,但我更喜欢自己构建和管理对象,使用的模式与Maxm007在其答案中概述的模式不同 . 我发现使用第三方容器有两个主要问题:

    1)让第三方库“自动”管理对象的生命周期可能会导致意外结果 . 我们发现,特别是在大型项目中,您可以拥有比预期更多的对象副本,并且比手动管理生命周期更多 . 我确信这取决于所使用的框架,但问题仍然存在 . 如果您的对象包含资源,数据连接等,这也可能会有问题,因为对象有时可能比您预期的更长寿 . 因此,IoC容器不可避免地会增加应用程序的资源利用率和内存占用量 .

    2)在我看来,IoC容器是“黑盒编程”的一种形式 . 我发现,特别是我们经验不足的开发人员倾向于滥用它们 . 它允许程序员不必考虑对象应该如何相互关联或如何将它们分离,因为它为它们提供了一种机制,在这种机制中,它们可以简单地凭空捕获任何对象 . 例如,可能有一个很好的设计理由,即ObjectA不应该直接了解ObjectB,而是创建工厂或桥接器或服务定位器,没有经验的程序员只会说“没问题,我只是从IoC容器中获取ObjectB” ” . 这实际上可以导致增加对象耦合,这是IoC应该帮助防止的 .

  • 2

    ASP.NET项目中的依赖注入可以通过几行代码完成 . 我认为当你有一个使用多个前端并需要单元测试的应用程序时,使用容器有一些优势 .

相关问题