The best definition I've found so far is one by James Shore:
"Dependency Injection" is a 25-dollar
term for a 5-cent concept. [...]
Dependency injection means giving an
object its instance variables. [...].
There is an article by Martin Fowler that may prove useful, too.
Dependency injection is basically providing the objects that an object needs (its dependencies) instead of having it construct them itself. It's a very useful technique for testing, since it allows dependencies to be mocked or stubbed out.
Dependencies can be injected into objects by many means (such as constructor injection or setter injection). One can even use specialized dependency injection frameworks (e.g. Spring) to do that, but they certainly aren't required. You don't need those frameworks to have dependency injection. Instantiating and passing objects (dependencies) explicitly is just as good an injection as injection by framework.
For myself one of the main reasons to use an IoC (and make use of external configuration) is around the two areas of:
- Testing
- Production maintenance
Testing
If you split your testing into 3 scenarios (which is fairly normal in large scale development):
- Unit testing
- Integration testing
- Black box testing
What you will want to do is for the last two test scenarios (Integration & Black box), is not recompile any part of the application.
If any of your test scenarios require you to change the configuration (ie: use another component to mimic a banking integration, or do a performance load), this can be easily handled (this does come under the benefits of configuring the DI side of an IoC though.
Additionally if your app is used either at multiple sites (with different server and component configuration) or has a changing configuration on the live environment you can use the later stages of testing to verify that the app will handle those changes.
Production
As a developer you don't (and should not) have control of the production environment (in particular when your app is being distributed to multiple customers or seperate sites), this to me is the real benefit of using both an IoC and external configuration, as it is up to the infrastructure/production support to tweak and adjust the live environment without having to go back to developers and through test (higher cost when all they want to do is move a component).
Summary
The main benefits that external configuration of an IoC come from giving others (non-developers) the power to configure your application, in my experience this is only useful under a limited set of circumstances:
- Application is distributed to multiple sites/clients where environments will differ.
- Limited development control/input over the production environment and setup.
- Testing scenarios.
In practice I've found that even when developing something that you do have control over the environment it will be run on, over time it is better to give someone else the capabilities to change the configuration:
- When developing you don't know when it will change (the app is so useful your company sells it to someone else).
- I don't want to be stuck with changing the code every time a slight change is requested that could have been handled by setting up and using a good configuration model.
Note: Application refers to the complete solution (not just the executable), so all files required for the application to run.
Best Answer
First, I want to explain an assumption that I make for this answer. It is not always true, but quite often:
(Actually, there are interfaces that are nouns as well, but I want to generalize here.)
So, e.g. an interface may be something such as
IDisposable
,IEnumerable
orIPrintable
. A class is an actual implementation of one or more of these interfaces:List
orMap
may both be implementations ofIEnumerable
.To get the point: Often your classes depend on each other. E.g. you could have a
Database
class which accesses your database (hah, surprise! ;-)), but you also want this class to do logging about accessing the database. Suppose you have another classLogger
, thenDatabase
has a dependency toLogger
.So far, so good.
You can model this dependency inside your
Database
class with the following line:and everything is fine. It is fine up to the day when you realize that you need a bunch of loggers: Sometimes you want to log to the console, sometimes to the file system, sometimes using TCP/IP and a remote logging server, and so on ...
And of course you do NOT want to change all your code (meanwhile you have gazillions of it) and replace all lines
by:
First, this is no fun. Second, this is error-prone. Third, this is stupid, repetitive work for a trained monkey. So what do you do?
Obviously it's a quite good idea to introduce an interface
ICanLog
(or similar) that is implemented by all the various loggers. So step 1 in your code is that you do:Now the type inference doesn't change type any more, you always have one single interface to develop against. The next step is that you do not want to have
new Logger()
over and over again. So you put the reliability to create new instances to a single, central factory class, and you get code such as:The factory itself decides what kind of logger to create. Your code doesn't care any longer, and if you want to change the type of logger being used, you change it once: Inside the factory.
Now, of course, you can generalize this factory, and make it work for any type:
Somewhere this TypeFactory needs configuration data which actual class to instantiate when a specific interface type is requested, so you need a mapping. Of course you can do this mapping inside your code, but then a type change means recompiling. But you could also put this mapping inside an XML file, e.g.. This allows you to change the actually used class even after compile time (!), that means dynamically, without recompiling!
To give you a useful example for this: Think of a software that does not log normally, but when your customer calls and asks for help because he has a problem, all you send to him is an updated XML config file, and now he has logging enabled, and your support can use the log files to help your customer.
And now, when you replace names a little bit, you end up with a simple implementation of a Service Locator, which is one of two patterns for Inversion of Control (since you invert control over who decides what exact class to instantiate).
All in all this reduces dependencies in your code, but now all your code has a dependency to the central, single service locator.
Dependency injection is now the next step in this line: Just get rid of this single dependency to the service locator: Instead of various classes asking the service locator for an implementation for a specific interface, you - once again - revert control over who instantiates what.
With dependency injection, your
Database
class now has a constructor that requires a parameter of typeICanLog
:Now your database always has a logger to use, but it does not know any more where this logger comes from.
And this is where a DI framework comes into play: You configure your mappings once again, and then ask your DI framework to instantiate your application for you. As the
Application
class requires anICanPersistData
implementation, an instance ofDatabase
is injected - but for that it must first create an instance of the kind of logger which is configured forICanLog
. And so on ...So, to cut a long story short: Dependency injection is one of two ways of how to remove dependencies in your code. It is very useful for configuration changes after compile-time, and it is a great thing for unit testing (as it makes it very easy to inject stubs and / or mocks).
In practice, there are things you can not do without a service locator (e.g., if you do not know in advance how many instances you do need of a specific interface: A DI framework always injects only one instance per parameter, but you can call a service locator inside a loop, of course), hence most often each DI framework also provides a service locator.
But basically, that's it.
P.S.: What I described here is a technique called constructor injection, there is also property injection where not constructor parameters, but properties are being used for defining and resolving dependencies. Think of property injection as an optional dependency, and of constructor injection as mandatory dependencies. But discussion on this is beyond the scope of this question.