我一直使用 依赖注射 (二)一段时间,注射者中的一个构造、财产或方法。我从未觉得有需要使用一种 反转的控制 (IoC)容器。但是,我阅读,更多的压力我感觉自社会利用一个IoC容器。

我打了.净容器样 StructureMap, , 团结, , Funq.我仍然看不到如何IoC容器要好处/提高我的代码。

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

请说服我,我需要用一个IoC容器。我要利用这些论点的时候我谈到我的同胞开发人员的工作。

有帮助吗?

解决方案

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

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 现在都会在相关对象的每个虚拟属性设置器上自动为您生成。

这是魔法吗? 是的!如果您可以相信此代码能够完成其工作,那么您可以安全地跳过所有属性包装的繁琐内容。您有业务问题需要解决。

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

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

其他提示

我和你在一起,瓦迪姆。 IoC容器举一个简单的,优雅的和有用的概念,并使其你有两天了200页的手册学习一些东西。

我个人在社区的IoC由Martin Fowler 优雅文章困惑一>化成了一串复杂的框架通常与200-300页的手册。

我尽量不主观臆断(哈哈!),但我认为,谁使用IoC容器的人(A)很聪明,(B)在同情缺乏对人谁不聪明,因为它们。一切都非常有意义给他们,所以他们很难理解,许多普通程序员会发现概念混乱。据记者了解的诅咒。谁了解IoC容器的人很难相信还有人谁不明白。

使用IoC容器的最有价值的好处是,可以在一个地方,它可以让你之间,比方说,测试模式和生产模式改变具有这样的结构的开关。例如,假设你有你的数据库访问类的两个版本......一个版本,它积极记录,并做了很多的验证,这在开发过程中使用,而另一个版本没有记录或验证,这是惊人快速进行生产。我很高兴能够在一个地方它们之间进行切换。在另一方面,这是更简单的方式容易地处理而不IoC容器的复杂性相当微不足道的问题。

我相信,如果你使用IoC容器,你的代码变得坦率地说,有很多难以阅读。的名额,你必须看找出代码试图做由至少一个上升。在天上某处天使呼喊。

在我看来,一个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类是这样的,我在这里使用统一。

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

IOC容器也很好,用于加载深深嵌套类的依赖关系。例如,如果你使用Depedency注射具有以下代码。

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

我的声明式编程的风扇(看我有多少SQL问题回答),但IoC容器我看似乎对自己好太神秘。

...或也许IoC容器的开发者不能字迹清晰的文档。

...否则均为真到一个程度或另一个。

我不认为的 IoC容器的概念的是坏的。但实现具有既强大(即,柔性的)足够在各种各样的应用中,简单和容易理解的是有用的。

这可能是半斤八两。实际的应用程序(而不是玩具或演示)注定是复杂的,占许多其他的情况和异常到的规则。要么你封装,在必要的代码的复杂性,或者在声明代码。但你必须从某个地方代表它。

使用容器主要是从 命令式/脚本化 初始化和配置的风格 陈述性的 一。这可能会产生一些不同的有益效果:

  • 减少 毛团 主程序启动例程。
  • 启用相当深入的部署时重新配置功能。
  • 使依赖注入风格成为新工作阻力最小的途径。

当然,可能会有困难:

  • 需要复杂的启动/关闭/生命周期管理的代码可能不容易适应容器。
  • 您可能需要解决任何个人、流程和团队文化问题 - 但是,这就是您问的原因......
  • 一些工具包本身正在迅速变得重量级,从而鼓励了许多 DI 容器最初作为强烈反对的深度依赖。

对我来说听起来像 你已经构建了自己的 IoC 容器 (使用 Martin Fowler 描述的各种模式)并询问为什么其他人的实现比你的更好。

所以,你有一堆已经可以工作的代码。并且想知道为什么您想用其他人的实现来替换它。

优点 考虑第三方 IoC 容器

  • 您可以免费修复错误
  • 图书馆的设计可能比你的更好
  • 人们可能已经熟悉特定的图书馆
  • 图书馆可能比你的快
  • 它可能有一些您希望实现但从未有时间实现的功能(您有服务定位器吗?)

缺点

  • 您可以免费获得引入的错误:)
  • 图书馆的设计可能比你的更糟糕
  • 你必须学习一个新的API
  • 太多的功能你永远用不到
  • 调试不是您编写的代码通常更困难
  • 从以前的 IoC 容器迁移可能很乏味

因此,权衡利弊并做出决定。

我认为 IoC 的大部分价值是通过使用 DI 获得的。既然您已经这样做了,那么剩下的好处就是增量的。

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

  • 对于多租户,IoC 容器可以处理一些用于加载不同客户端资源的基础设施代码。当您需要特定于客户端的组件时,请使用自定义选择器来处理逻辑,而不必担心客户端代码中的问题。你当然可以自己构建这个,但是 这是一个例子 国际奥委会如何提供帮助。

  • IoC 具有许多可扩展性,可用于从配置加载组件。这是构建的常见内容,但工具是由容器提供的。

  • 如果你想使用 AOP 来解决一些横切问题, IoC提供钩子 拦截方法调用。这在项目中不太常见,但 IoC 使其变得更容易。

我以前编写过类似的功能,但如果我现在需要这些功能中的任何一个,我宁愿使用预先构建和测试的工具(如果它适合我​​的架构)。

正如其他人所提到的,您还可以集中配置要使用的类。尽管这可能是一件好事,但它的代价是误导和复杂化。大多数应用程序的核心组件不会被太多替换,因此做出权衡有点困难。

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

我是一个正在康复的国际奥委会瘾君子。我发现现在在大多数情况下很难证明使用 IOC 进行 DI 是合理的。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 容器配置模块替换工厂实现即可。

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

我没有编程的网络,我的是一个控制台应用程序,并在许多地方深的对象树,我需要有机会获得由被上的一个完全独立的分支创建的用户指定的设置或元数据对象树。使用IoC,我只是告诉Ninject对待设置为单(因为总是有他们的只有一个实例),要求设置或字典在构造和急......当我需要他们,他们奇迹般地出现了!

在不使用IoC容器我将不得不通过传递的设置和/或元数据向下2,3,...,n个对象,然后将其实际使用由需要它的对象。

有到DI / IoC容器许多其他好处如其他人已经在这里详细介绍,并从创建对象请求对象可以是令人费解的想法移动,但使用DI是非常有益的,我和我的团队,所以也许你可以将其添加到您的阿森纳!

如果您想...,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 和 is 应该是一个警告信号,表明您的设计有问题(如果您得到了它)。

要摆脱它(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 框架的人非常聪明 - 我只是相信他们不懂数学。

我发现正确实现依赖注入往往会迫使程序员使用各种其他编程实践来帮助提高代码的可测试性、灵活性、可维护性和可扩展性:诸如单一职责原则、关注点分离以及针对 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 尝试通过每个请求的反射来实例化控制器。要手动进行依赖注入,您必须 每个控制器 “上下文根”,如下所示:

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 框架,请注意:

  • 我可以对这门课进行单元测试。通过创建一个模拟 ISimpleWorkflowInstanceMerger, ,我可以测试它是否按照我预期的方式使用,而不需要数据库连接或任何东西。
  • 我使用的代码少得多,而且代码更容易阅读。
  • 如果我的依赖项之一发生更改,我不必对控制器进行任何更改。当您考虑到多个控制器可能使用某些相同的依赖项时,这尤其好。
  • 我从未明确引用数据层中的类。我的 Web 应用程序只能包含对包含以下内容的项目的引用 ISimpleWorkflowInstanceMerger 界面。这使我能够将应用程序分解为单独的模块,并维护真正的多层架构,从而使事情变得更加灵活。

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

当你使用了“新”的关键字,你要创建一个具体类的依赖和一点点的警钟应该在你的头上熄灭。它变得难以进行隔离测试此对象。解决的办法是对接口进行编程和注入的依赖性,使得对象可以与任何实现该接口进行单元测试(例如,嘲笑)。

麻烦的是,你必须从某个地方构造对象。工厂模式是耦合移出你的POXOs的(普通老式“在这里插入你的面向对象的语言”对象)的一种方式。如果你和你的同事都是这样写代码,然后IoC容器是下一个“增量改进”你可以让你的代码库。它会转移所有讨厌的工厂样板代码你的清洁对象和业务逻辑。他们会得到它,喜欢它。哎呀,给你为什么喜欢它,并让每个人都热情一家公司谈。

如果你的同事都没有做DI呢,那么我建议你专注于第一。传播如何编写干净的代码,很容易检验的字。清洁DI码是困难的部分,一旦你在那里,从工厂的类对象布线逻辑转移到IoC容器应该是相对微不足道的。

由于所有的依赖都清晰可见,它促进创建诸松散耦合,并在同一时间方便和可重复使用的整个应用程序组件。

您不要的需要的IoC容器。

但是,如果你严格遵循DI模式,你会发现,有一个将删除一吨的多余的,枯燥的代码。

这是经常使用的库/框架,反正最好的时机 - 当你理解它在做什么,并能做到这一点,而不库

我只是这么恰巧在猛拉家庭长大的DI代码,并与国际奥委会替换它的过程。我可能已经删除了超过200行代码,并与约10是取代它,我必须做学习如何使用容器(南联)的一点点,但我是一个工程师,在互联网技术方面的工作21世纪,所以我习惯了。我大概花了大约20分钟看在密技。这是非常值得我的时间。

当你继续你的脱钩类和反转的依赖关系,这些类继续保持小型和“依赖图”继续扩大规模。 (这是不坏。)使用IoC容器的基本特征,使得接线,所有这些对象微不足道,但做手工可以得到非常沉重的负担。例如,如果我想要的东西,打造“富”的新实例,但它需要一个“酒吧”。和 “酒吧” 需要一个 “A”, “B” 和 “C”。每一类又需要其他的三样东西,等等等等(是的,我不能拿出好的假名字:))。

使用IoC容器来构建对象图你降低了复杂性一吨,并推动它伸到一次性配置。我简单地说“创造了我一个‘富’”,并计算出打造一个我们所需要的。

有人使用IoC容器得多更多的基础设施,这是罚款,先进的场景,但在这种情况下我同意它可以混淆,使代码难以阅读和调试新的开发者。

Dittos约统一。受到太大了,你可以听到椽吱吱作响。

它从来没有让我惊讶,当人们开始喷出了约IoC的代码看起来何洁是同样类型的人谁一次谈到了于C模板如何++是优雅的路要走,早在上世纪90年代,但现在将谴责的他们作为神秘。呸!

在.NET世界AOP是不是太受欢迎,所以对DI框架是你唯一的选择,不管你写一个自己或使用其他框架。

如果您使用AOP您可以在编译应用程序,这是比较常见的Java中注入。

有到DI诸多好处,如减少耦合,单元测试是比较容易的,但你将如何实现呢?你想使用反射来自己做呢?

那么,已经快3年了,是吗?

  1. 50% 的支持 IoC 框架的人不理解 IoC 和 IoC 框架之间的区别。我怀疑他们是否知道您可以编写代码而无需部署到应用程序服务器

  2. 如果我们采用最流行的 Java Spring 框架,它是将 IoC 配置从 XMl 移到代码中,现在看起来像这样

`@configuration public Class appconfig {

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

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

}`而且我们需要一个框架来做到这一点,为什么?

老实说,我不觉得那里是很多情况下,IoC容器都需要,大部分时间,他们只是增加不必要的复杂性。

如果您使用的是它只是用来制造物体的简单的建设,我不得不问,你在多个位置实例化此对象吗?将一个单无法满足您的需求?你改变在运行时配置? (开关数据源类型,等等)。

如果是,那么你可能需要一个IoC容器。如果没有,那么你只是移动初始化远离那些开发者可以很容易地看到它。

谁说的界面是不是继承更好呢?假设你正在测试服务。为什么不使用构造DI,并创建使用继承依赖的嘲笑?我使用的大多数服务只有少数的依赖。这样做单元测试这种方式阻止保持一吨的无用的接口,意味着你不需要的的使用ReSharper的快速查找方法的声明。

相信对于大多数实现中,说IOC容器删除不需要的代码是一个神话。

首先,有设置在容器中的第一位。然后,你还必须定义需要初始化每个对象。所以,你不保存代码初始化,你动它(除非你的对象被多次使用。它是更好的单身?)。然后,以这种方式已经初始化每一个对象,你必须创建和维护一个接口。

任何人有什么想法?

如果您需要集中你依赖的配置,使他们可以很容易地换出连接质量,您将需要一个IoC容器。这使得TDD,许多依赖性换出最合理的,但如果有依赖关系之间的相互依存关系不大。这是在混淆对象构造的控制在一定程度上流动的成本完成,因此具有良好的组织和记录合理配置是非常重要的。这也是好事,有一个理由这样做,否则就是单纯的抽象金 - 电镀。我已经看到了这样做的很差,所以它拖累相当于被用于构造goto语句。

这里是为什么。该项目被称为国际奥委会与 - Ninject。您可以下载并使用Visual Studio运行它。此示例使用Ninject,但所有的“新”的语句是在一个位置,你可以彻底改变通过改变模块绑定应用程序运行时如何使用。该示例设置,使您可以绑定到服务的嘲笑版本或真实版本。在小型项目,可能并不重要,但在大项目这是一个大问题。

只要是明确的,优点我看到他们: 1)的所有的 在的代码根一个位置语句。 2)完全重新因子码与一个变化。 3)“酷因素”“加分,导致它的......嗯:爽。 :P

我会尝试从我的角度找出为什么国际奥委会可能不适合。

与其他所有事物一样,IOC 容器(或者爱因斯坦所说的 I=OC^2)是一个您必须自己决定代码中是否需要它的概念。最近时尚界对国际奥委会的强烈抗议仅此而已。不要迷恋时尚,这是第一。您可以在代码中实现无数的概念。首先,自从我开始编程以来,我就在使用依赖注入,并且当它以这个名称流行时学习了这个术语本身。依赖控制是一个非常古老的主题,迄今为止已经以数万亿种方式解决了这个问题,具体取决于什么与什么脱钩。将一切与一切脱钩是无稽之谈。IOC 容器的问题在于它试图与实体框架或 NHibernate 一样有用。虽然只要您必须将任何数据库与系统耦合,就必须编写对象关系映射器,但 IOC 容器并不总是必要的。所以当IOC容器有用时:

  1. 当您遇到需要组织许多依赖项的情况时
  2. 当您不关心将代码与第三方产品耦合时
  3. 当您的开发人员想要学习如何使用新工具时

1:代码中存在如此多的依赖项,或者您在设计之初就意识到它们的情况并不常见。当抽象思维到期时,抽象思维很有用。

2:将您的代码与第三方代码耦合是一个巨大的问题。我正在使用已有 10 多年历史的代码,并且遵循当时奇特且先进的概念 ATL、COM、COM+ 等。现在您无法对该代码执行任何操作。我的意思是,先进的理念具有明显的优势,但从长远来看,这种优势会被过时的优势本身所抵消。它只是让一切变得更加昂贵。

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

关于 IOC2,到处都写着“我们知道如何”综合症。这与自动化测试类似。乍一看很花哨的术语和完美的解决方案,您只需在夜间执行所有测试即可在早上看到结果。向一家又一家公司解释自动化测试的真正含义确实很痛苦。自动化测试绝对不是减少错误数量的快速方法,您可以在一夜之间引入错误数量以提高产品质量。但是,时尚正在让这种观念令人恼火地占据主导地位。IOC2 也有同样的症状。人们相信,为了让你的软件变得更好,你需要实现它。最近的每次采访我都会被问到是否正在实施 IOC2 和自动化。这就是时尚的标志:该公司有一些用 MFC 编写的代码,他们不会放弃。

您需要像软件中的任何其他概念一样学习 IOC2。是否需要使用 IOC2 的决定由团队和公司内部决定。然而,在做出决定之前,至少必须提及所有上述论点。只有当你看到积极的一面大于消极的一面时,你才能做出积极的决定。

IOC2没有什么问题,只是它确实只解决了它解决的问题并引入了它引入的问题。没有其他的。然而,逆潮流而行是非常困难的,他们有汗口,什么都有追随者。奇怪的是,当他们的幻想问题变得明显时,他们却没有在场。软件行业的许多概念都受到了捍卫,因为它们创造了利润、撰写了书籍、召开了会议、制造了新产品。这就是时尚,通常是短暂的。人们一旦发现其他东西,就会完全放弃它。IOC2 很有用,但它显示出与我见过的许多其他消失的概念相同的迹象。我不知道它能否生存。对此没有任何规则。你认为如果它有用,它就会生存。不,事情并非如此。一家富有的大公司就足够了,这个概念可能会在几周内消亡。我们拭目以待。NHibernate 幸存下来,EF 屈居第二。也许IOC2也会幸存下来。不要忘记,软件开发中的大多数概念没什么特别的,它们非常有逻辑、简单且明显,有时记住当前的命名约定比理解概念本身更困难。IOC2 的知识是否能让开发人员成为更好的开发人员?不,因为如果开发人员无法提出本质上与 IOC2 类似的概念,那么他或她将很难理解 IOC2 正在解决哪个问题,使用它会显得人为,他或她可能会开始使用它为了某种政治正确。

就个人而言,我使用的IoC作为某种我的应用程序的结构图的(是的,我还优选StructureMap;))。它可以很容易在测试过程中,以替代与起订量实现我ussual接口实现。创建一个测试设置可以作为制备新的INIT-呼叫到我的IoC的框架,用取其类是我的测试边界用mock一样容易。

这是国际奥委会可能是不是在那里,但它是什么,我发现自己使用它的最..

IOC容器解决的一个问题你可能没有但这是一个很好的问题

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

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

我意识到这是一个比较旧的文章,但它似乎仍然是相当活跃的,我想我会有助于那些尚未在其他的答案中提到了几个点的。

我会说,我同意依赖注入的好处,但我更愿意建设和管理的对象自己,使用模式没有什么不同,通过Maxm007在他的回答中概述。我已经发现了两个主要问题是使用第三方容器:

1)有一个第三方库管理您的对象生命周期“自动地”可以借给自己意想不到的结果。我们发现,特别是在大型项目中,你可以有比您预期的对象得多份,超过你,如果你是手动管理的生命周期。我敢肯定,这取决于所使用的架构不同,但问题仍然存在。这也可能是有问题的,如果你的对象持有的资源,数据连接等,因为对象可有时住比预期的更长。因此不可避免地,IoC容器趋向于增加的应用程序的资源利用率和内存占用量。

2)IoC容器,在我看来,是“黑盒子编程”的一种形式。我发现,特别是我们经验不足的开发人员往往虐待他们。它允许程序员不必考虑对象应该如何相互关联或如何去耦,因为它提供了他们,使他们能够简单地抓取自己想要凭空任何物体的机制。例如,有可能是一个很好的设计原因,对象A不应该知道对象B直接,但不是建立一个工厂或桥梁或服务定位器,一个没有经验的程序员会简单地说:“没问题,我就从IoC容器抢对象B ”。这实际上可以导致增加的对象耦合,这是国际奥委会应该有助于预防。

在ASP.NET项目依赖注入可以用的几行代码来实现。我想有一些优势,使用的容器,当你有一个使用多个前端,并且需要单元测试的应用程序。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top