Dependency injection
From Wikipedia, the free encyclopedia
This article includes a list of references or external links, but its sources remain unclear because it lacks inline citations. Please improve this article by introducing more precise citations where appropriate. (October 2007) |
Dependency Injection (DI) in computer programming refers to the process of supplying an external dependency to a software component. It is a specific form of inversion of control where the concern being inverted is the process of obtaining the needed dependency. The term was first coined by Martin Fowler to more clearly describe the mechanism.[1]
Contents |
[edit] Basics
Conventionally, if an object needs to gain access to a particular service, the object takes responsibility to access that service: either it holds a direct reference to the location of that service, or it goes to a known service locator and requests that it be passed back a reference to an implementation of a specified type of service. By contrast, using dependency injection, the object simply provides a property that can hold a reference to that type of service; then, when the object is created, a reference to an implementation of that type of service will automatically be injected into that property by an external mechanism.
When the dependency injection technique is used to decouple high-level modules from low-level services, the resulting design guideline is called the dependency inversion principle.
The dependency injection approach offers more flexibility because it becomes easier to create alternative implementations of a given service type, and then to specify which implementation is to be used via a configuration file, without any change to the objects that use the service. This is especially useful in unit testing, because it is easy to inject a mock implementation of a service into the object being tested.
On the other hand, excessive use of dependency injection can make applications more complex and harder to maintain: in order to understand the application's behaviour the developer needs to look at the configuration as well as the code, and the configuration is "invisible" to IDE-supported reference analysis and refactoring unless the IDE specifically supports the dependency injection framework. Frameworks such as the Grok web framework introspect the code and use convention over configuration as an alternative form of deducing configuration information. For example, if a Model and View class were in the same module, then an instance of the View will be created with the appropriate Model instance passed into the constructor.
[edit] Code illustration using Java
Suppose that Foo
is an interface:
public interface Foo { void bar(); // Perform bar void baz(); // Perform baz }
There exist also a number of implementation classes, each of them implementing Foo
in some way:
public class DatabaseFoo implements Foo { void bar() { Database.selectBar().execute(); // Use the database to do bar } void baz() { Database.selectBaz().run(); // Use the database to do baz } }
public class PixieDustFoo implements Foo { void bar() { Spell.cast("bar"); // Magic! } void baz() { Spell.cast("baz"); // More magic! } }
Foo
only specifies the operations available in its interface, but doesn't itself provide any implementation, instead leaving that to other implementer classes. This way a user wishing to use the Foo
functionality can use any implementation, not knowing anything more about them than that they conform to the Foo interface.
An object needing the services defined by Foo
needs to get an instance of a class that implements Foo
:
public class ImportantClass { Foo foo; public ImportantClass() { this.foo = new DatabaseFoo(); } void doReallyImportantStuff() { this.foo.bar(); } }
However, this defeats the entire point of using an interface instead of a concrete implementation. To fix that, it's enough to let the outside caller provide the desired implementation:
public class ImportantClass { Foo foo; public ImportantClass(Foo foo) { this.foo = foo; } void doReallyImportantStuff() { this.foo.bar(); } }
When using dependency injection there is usually a configuration mechanism or architecture for deciding which implementation gets injected into an object.
[edit] Types
Fowler identifies three ways in which an object can get a reference to an external module, according to the pattern used to provide the dependency:[2]
- Type 1 or interface injection, in which the exported module provides an interface that its users must implement in order to get the dependencies at runtime.
- Type 2 or setter injection, in which the dependent module exposes a setter method which the framework uses to inject the dependency.
- Type 3 or constructor injection, in which the dependencies are provided through the class constructor. This is the main form used by PicoContainer, although it also supports setter injection.
It is possible for other frameworks to have other types of injection, beyond those presented above.[3]
[edit] Existing frameworks
Dependency injection frameworks exist for a number of platforms and languages, as can be seen in the following table:
[edit] See also
[edit] Further reading
- A beginners guide to Dependency Injection
- What is Dependency Injection? - An alternative explanation - Jakob Jenkov
- Dependency Injection & Testable Objects: Designing loosely coupled and testable objects - Jeremy Weiskotten; Dr. Dobb's Journal, May 2006.
- Design Patterns: Dependency Injection -- MSDN Magazine, September 2005
- Writing More Testable Code with Dependency Injection -- Developer.com, October 2006
- Domain Specific Modeling (DSM) in IOC frameworks
- The Rich Engineering Heritage Behind Dependency Injection - Andrew McVeigh - A detailed history of dependency injection.
- P of EAA: Plugin