Why do I need an IoC container as opposed to strai

2018-12-31 06:12发布

I've been using Dependency Injection (DI) for a while, injecting either in a constructor, property, or method. I've never felt a need to use an Inversion of Control (IoC) container. However, the more I read, the more pressure I feel from the community to use an IoC container.

I played with .NET containers like StructureMap, NInject, Unity, and Funq. I still fail to see how an IoC container is going to benefit / improve my code.

I'm also afraid to start using a container at work because many of my co-workers will see code which they don't understand. Many of them may be reluctant to learn new technology.

Please, convince me that I need to use an IoC container. I'm going to use these arguments when I talk to my fellow developers at work.

30条回答
临风纵饮
2楼-- · 2018-12-31 06:17

In my opinion the number one benefit of an IoC is the ability to centralize the configuration of your dependencies.

If you're currently using Dependency injection your code might look like this

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

If you used a static IoC class, as opposed to the, IMHO the more confusing, configuration files, you could have something like this:

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
}

Then, your Static IoC class would look like this, I'm using Unity here.

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.
   }
}
查看更多
孤独寂梦人
3楼-- · 2018-12-31 06:21

IoC Containers are also good for loading deeply nested class dependencies. For example if you had the following code using 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 { }

If you had all of these dependencies loaded into and IoC container you could Resolve the CustomerService and the all the child dependencies will automatically get resolved.

For example:

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.
}
查看更多
ら面具成の殇う
4楼-- · 2018-12-31 06:21

Because all the dependencies are clearly visible, it promotes creating components which are loosely coupled and at the same time easily accessible and reusable across the application.

查看更多
余欢
5楼-- · 2018-12-31 06:21

So, it's been almost 3 years, eh?

  1. 50% of those who commented in favor of IoC frameworks don't understand the difference between IoC and IoC Frameworks. I doubt they know that you can write code without being deployed to an app server

  2. If we take most popular Java Spring framework, it is IoC configuration moved from XMl into the code and now look like this

`@Configuration public class AppConfig {

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

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

} ` And we need a framework to do this why exactly?

查看更多
伤终究还是伤i
6楼-- · 2018-12-31 06:21

Honestly I don't find there to be many cases where IoC containers are needed, and most of the time, they just add unneeded complexity.

If you are using it just for making construction of an object simpler, I'd have to ask, are you instantiating this object in more than one location? Would a singleton not suit your needs? Are you changing the configuration at runtime? (Switching data source types, etc).

If yes, then you might need an IoC container. If not, then you're just moving the initialization away from where the developer can easily see it.

Who said that an interface is better than inheritance anyway? Say you're testing a Service. Why not use constructor DI, and create mocks of the dependencies using inheritance? Most services I use only have a few dependencies. Doing unit testing this way prevents maintaining a ton of useless interfaces and means you don't have to use Resharper to quickly find the declaration of a method.

I believe that for most implementations, saying that IoC Containers remove unneeded code is a myth.

First, there's setting up the container in the first place. Then you still have to define each object that needs to be initialized. So you don't save code in initialization, you move it (unless your object is used more than once. Is it better as a Singleton?). Then, for each object you've initialized in this way, you have to create and maintain an interface.

Anyone have any thoughts on this?

查看更多
步步皆殇っ
7楼-- · 2018-12-31 06:22

I've found that correctly implementing Dependency Injection tends to force programmers to use a variety of other programming practices that help to improve the testability, flexibility, maintainability, and scalability of code: practices like the Single Responsibility Principle, Separations of Concerns, and coding against APIs. It feels like I'm being compelled to write more modular, bite-sized classes and methods, which makes the code easier to read, because it can be taken in bite-sized chunks.

But it also tends to create rather large dependency trees, which are far more easily managed via a framework (especially if you use conventions) than by hand. Today I wanted to test something really quickly in LINQPad, and I figured it'd be too much bother to create a kernel and load in my modules, and I ended up writing this by hand:

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

In retrospect, it would have been quicker to use the IoC framework, since the modules define pretty much all of this stuff by convention.

Having spent some time studying the answers and comments on this question, I am convinced that the people who are opposed to using an IoC container aren't practicing true dependency injection. The examples I've seen are of practices that are commonly confused with dependency injection. Some people are complaining about difficulty "reading" the code. If done correctly, the vast majority of your code should be identical when using DI by hand as when using an IoC container. The difference should reside entirely in a few "launching points" within the application.

In other words, if you don't like IoC containers, you probably aren't doing Dependency Injection the way it's supposed to be done.

Another point: Dependency Injection really can't be done by hand if you use reflection anywhere. While I hate what reflection does to code navigation, you have to recognize that there are certain areas where it really can't be avoided. ASP.NET MVC, for example, attempts to instantiate the controller via reflection on each request. To do dependency injection by hand, you would have to make every controller a "context root," like so:

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

Now compare this with allowing a DI framework to do it for you:

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

Using a DI framework, note that:

  • I can unit-test this class. By creating a mock ISimpleWorkflowInstanceMerger, I can test that it gets used the way I anticipate, without the need for a database connection or anything.
  • I use far less code, and the code is much easier to read.
  • If one of my dependency's dependency's changes, I don't have to make any changes to the controller. This is especially nice when you consider that multiple controllers are likely to use some of the same dependencies.
  • I never explicitly reference classes from my data layer. My web application can just include a reference to the project containing the ISimpleWorkflowInstanceMerger interface. This allows me to break the application up into separate modules, and maintain a true multi-tier architecture, which in turn makes things much more flexible.

A typical web application will have quite a few controllers. All of the pain of doing DI by hand in each controller will really add up as your application grows. If you have an application with only one context root, which never tries to instantiate a service by reflection, then this isn't as big a problem. Nevertheless, any application that uses Dependency Injection will become extremely expensive to manage once it reaches a certain size, unless you use a framework of some kind to manage the dependency graph.

查看更多
登录 后发表回答