Understanding the need for a DI framework

前端 未结 12 1783
长情又很酷
长情又很酷 2020-12-07 12:39

This might be a naive question. I\'m currently learning the Spring framework and dependency injection. While the basic principle of DI is rather easy to grasp, it\'s

相关标签:
12条回答
  • 2020-12-07 13:12

    Dependency injection is a degenerate form of implicit parameter passing, and the purpose is essentially the same, to solve what's called The Configurations Problem:

    The configurations problem is to propagate run-time preferences throughout a program, allowing multiple concurrent configuration sets to coexist safely under statically guaranteed separation.

    Dependency Injection frameworks compensate for the lack of implicit parameters, Curried functions, and convenient facilities for monads in the language.

    0 讨论(0)
  • 2020-12-07 13:14

    One thing that most (if not all) DI containers/libraries bring you in addition is the possibility to intercept methods for all instances created through DI.

    0 讨论(0)
  • 2020-12-07 13:14

    Lately there has been very much emphasis on DI frameworks, even so that the DI pattern is being forgotten. DI's principles as summarized by J. B. Rainsberger:

    It's simple: make dependencies explicit by requiring collaborators as parameters in the constructor. Repeat until you've pushed all decisions about which objects to create into the entry point. Of course, this only applies to Services (in the DDD sense). Done.

    As you've noticed, there is not much difference between configuring the dependencies manually vs. using a framework. With constructor injection, as shown below, your code example would have even less boilerplate and the compiler would force you to provide all required dependencies (and your IDE will probably type them for you).

    SawMill sawMill = new SawMill(new HandSaw());
    sawMill.run();
    

    A DI framework can reduce the boilerplate of creating factories and wiring the objects together, but at the same time it can also make it harder to find out that where each dependency is coming from - the DI framework's configuration is one more layer of abstraction to dig through, and your IDE might not be able to tell you where a particular constructor is being called from.

    An indirect disadvantage of DI frameworks is that they can make wiring the dependencies too easy. When you can't anymore feel pain from having lots of dependencies, you may just keep on adding more dependencies instead of rethinking the design of the application to reduce the coupling between the classes. Wiring the dependencies manually - especially in test code - makes it easier to notice when you have too many dependencies as the test setup becomes longer and it becomes harder to write unit tests.

    Some advantages of DI frameworks come from their support of advanced features such as AOP (for example Spring's @Transactional), scoping (though many times scoping with plain code will be enough) and pluggability (if a plugin framework is really needed).

    Recently I made an experiment of manual DI vs. framework-based DI. The progress and results are shown as screencasts in Let's Code Dimdwarf episodes 42 through 47. The project in question had a Guice-based plugin system for creating actors, which I then rewrote using manual DI, without Guice. The result was a much simpler and clearer implementation, and only a little bit more boilerplate.

    Synopsis: First try using just manual DI (preferably constructor injection). If there becomes lots of boilerplate, try to rethink the design to reduce the dependencies. Use a DI framework if some of its features provide value for you.

    0 讨论(0)
  • 2020-12-07 13:18

    I've had the exact same question, and it was answered by this:
    Granted, you could do what you've described in "Then you could simply do:..." (let's call that "class A"). However, that would couple class A to HandSaw, or to all dependencies needed from class SawMill. Why should A be coupled to HandSaw - or, if you take a more realistic scenario, why should my business logic be coupled to the JDBC connection implementation needed for the DAO layer?
    The solution I proposed then was "then move the dependencies one step further" - ok, so now I've got my view coupled to JDBC connection, where I should only deal with HTML (or Swing, pick your flavor).

    The DI framework, configured by an XML (or JavaConfig) solves this by letting you just "get the needed service". You don't care how it's initialized, what it needs to work - you just get the service object and activate it.

    Also, you have a misconception there regarding the "plus:" (where you do SawMill springSawMill = (SawMill)context.getBean("sawMill"); springSawMill.run();) - you don't need to get the sawMill bean from the context - the sawMill bean should've been injected into your object (class A) by the DI framework. so instead of ...getBean(...), you'd just go "sawMill.run()", not caring where it came from, who initialized it and how. For all you care, it could go straight to /dev/null, or test output, or real CnC engine... The point is - you don't care. All you care is your tiny little class A which should do what it's contracted to do - activate a saw mill.

    0 讨论(0)
  • 2020-12-07 13:19

    One of the biggest benefits of using Dependency Injection is that it makes it much easier to provide mocks or stubs of a class's dependencies when creating a unit test for that class. This allows you to test the class in isolation without depending on its collaborators.

    In your example, there is no way to mock or stub out Saw or SawMill in the class that where those are being instantiated. If Saw and SawMill were set via setters or the constructor, then you could pass in your own mock Saw and mock SawMill when running the unit test.

    0 讨论(0)
  • 2020-12-07 13:21

    Spring helps you understand and even encourages DI models. However, I don't believe you have to have Spring.

    You can have configuration files in Java which you can debug, refactor and perform code analysis on.

    I suggest you put these java configuration files in another package, but they are otherwise equivalent to XML configuration files. You can even load these file dynamically if that's important.

    0 讨论(0)
提交回复
热议问题