Dependency injection
Encyclopedia
Dependency injection (DI) is a design pattern
in object-oriented
computer programming
whose purpose is to improve testability of, and simplify deployment of components in very large software systems.
The Dependency Injection pattern involves at least three elements:
The dependent object describes what components it depends on to do its work. The injector decides what concrete classes satisfy the requirements of the dependent object, and provides them to the dependent.
In conventional software development the dependent object decides for itself what concrete classes it will use; in the dependency injection pattern, this decision is delegated to the "injector" which can choose to substitute different concrete class implementations of a dependency contract interface at run time rather than at compile time.
Unit testing of components in large software systems is difficult, because components under test often require the presence of a substantial amount of infrastructure and set up in order to operate at all. Dependency injection simplifies the process of bringing up a working instance of an isolated component for testing. Because components declare their dependencies, a test can automatically bring up only those dependent components required to perform testing.
More importantly, injectors can be configured to swap in simplified "mock" implementations of dependent components when testing -- the idea being that the component under test can be tested in isolation as long as the substituted dependent components implement the contract of the dependent interface sufficiently to perform the unit test in question.
As an example, consider an automatic stock trading program that communicates with a live online trading service and stores historical analytic data in a distributed database. To test the component which recommends trades, one would ordinarily need to have a connection to the online service, and an actual distributed database, suitably populated with test data.
Using dependency injection, the components that provide access to the online service, and back-end databases could be replaced altogether with a test implementations of the dependency interface contracts that provide just enough behavior to perform tests on the component under test.
When using dependency injection, a consumer component specifies the service contract by interface, and the injector component selects an implementation on behalf of the dependent component.
In its simplest implementation, code that creates a dependent object supplies dependencies to that object via constructor arguments or by setting properties on the object.
More complicated implementations, such as Spring and Microsoft Managed Extensibility Framework (MEF), automate this procedure. These frameworks identify constructor arguments or properties on the objects being created as requests for dependent objects, and automatically inject constructor arguments or set properties with pre-constructed instances of dependencies as part of the process of creating the dependent object. The client makes a request to the dependency injection system for an implementation of a particular interface; the dependency injection system creates the object, automatically filling in dependencies as required.
The following interface contracts define the behavior of components in the sample system.
The VerySimpleStockTraderImpl class creates instances of the
In this example, MyApplication.main plays the role of dependency injector, selecting the concrete implementations of the dependencies required by VerySimpleStockTraderImpl, and supplying those dependencies via constructor injection.
using XML
or metadata
definitions. Refactoring the above example to use an external XML-definition framework:
In this case, a dependency injection service is used to retrieve an instance of
a class that implements the
As there are many ways to implement dependency injection, only a small subset of examples are shown here. Dependencies can be registered, bound, located, externally injected, etc., by many different means. Hence, moving dependency management from one module to another can be accomplished in many ways.
Another benefit is that it offers configuration flexibility because alternative implementations of a given service can be used without recompiling code. This is useful in unit testing, as it is easy to inject a fake implementation
of a service into the object being tested by changing the configuration file, or overriding component registrations at run-time.
It is possible for other frameworks to have other types of injection, beyond those presented above.
Design pattern (computer science)
In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. A design pattern is not a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that...
in object-oriented
Object-oriented programming
Object-oriented programming is a programming paradigm using "objects" – data structures consisting of data fields and methods together with their interactions – to design applications and computer programs. Programming techniques may include features such as data abstraction,...
computer programming
Computer programming
Computer programming is the process of designing, writing, testing, debugging, and maintaining the source code of computer programs. This source code is written in one or more programming languages. The purpose of programming is to create a program that performs specific operations or exhibits a...
whose purpose is to improve testability of, and simplify deployment of components in very large software systems.
The Dependency Injection pattern involves at least three elements:
- a dependent consumer,
- a declaration of a component's dependencies, defined as interface contracts,
- an injector (sometimes referred to as a provider or container) that creates instances of classes that implement a given dependency interfaces on request.
The dependent object describes what components it depends on to do its work. The injector decides what concrete classes satisfy the requirements of the dependent object, and provides them to the dependent.
In conventional software development the dependent object decides for itself what concrete classes it will use; in the dependency injection pattern, this decision is delegated to the "injector" which can choose to substitute different concrete class implementations of a dependency contract interface at run time rather than at compile time.
Motivation
The primary purpose of the dependency injection pattern is to allow selection among multiple implementations of a given dependency interface at runtime, or via configuration files, instead of at compile time. The pattern is particularly useful for providing "mock" test implementations of complex components when testing; but is often used for locating plugin components, or locating and initializing software services.Unit testing of components in large software systems is difficult, because components under test often require the presence of a substantial amount of infrastructure and set up in order to operate at all. Dependency injection simplifies the process of bringing up a working instance of an isolated component for testing. Because components declare their dependencies, a test can automatically bring up only those dependent components required to perform testing.
More importantly, injectors can be configured to swap in simplified "mock" implementations of dependent components when testing -- the idea being that the component under test can be tested in isolation as long as the substituted dependent components implement the contract of the dependent interface sufficiently to perform the unit test in question.
As an example, consider an automatic stock trading program that communicates with a live online trading service and stores historical analytic data in a distributed database. To test the component which recommends trades, one would ordinarily need to have a connection to the online service, and an actual distributed database, suitably populated with test data.
Using dependency injection, the components that provide access to the online service, and back-end databases could be replaced altogether with a test implementations of the dependency interface contracts that provide just enough behavior to perform tests on the component under test.
Basics
Without dependency injection, a consumer component that needs a particular service in order to accomplish a task must create an instance of a class that concretely implements the dependency interface.When using dependency injection, a consumer component specifies the service contract by interface, and the injector component selects an implementation on behalf of the dependent component.
In its simplest implementation, code that creates a dependent object supplies dependencies to that object via constructor arguments or by setting properties on the object.
More complicated implementations, such as Spring and Microsoft Managed Extensibility Framework (MEF), automate this procedure. These frameworks identify constructor arguments or properties on the objects being created as requests for dependent objects, and automatically inject constructor arguments or set properties with pre-constructed instances of dependencies as part of the process of creating the dependent object. The client makes a request to the dependency injection system for an implementation of a particular interface; the dependency injection system creates the object, automatically filling in dependencies as required.
Code illustration using Java
Using the stock trading example mentioned above, the following Java examples show how coupled (manually-injected) dependencies and framework-injected dependencies are typically staged.The following interface contracts define the behavior of components in the sample system.
Highly coupled dependency
The following example shows code with no dependency injection applied:The VerySimpleStockTraderImpl class creates instances of the
IStockAnalysisService
, and IOnlineBrokerageService
by hard-coding constructor references to the concrete classes that implement those services.Manually injected dependency
Refactoring the above example to use manual injection:In this example, MyApplication.main plays the role of dependency injector, selecting the concrete implementations of the dependencies required by VerySimpleStockTraderImpl, and supplying those dependencies via constructor injection.
Automatically injected dependency
There are several frameworks available that automate dependency management through delegation. Typically, this is done with a containerWeb container
Web container is the component of a web server that interacts with the servlets. A web container is responsible for managing the lifecycle of servlets, mapping a URL to a particular servlet and ensuring that the URL requester has the correct access rights...
using XML
XML
Extensible Markup Language is a set of rules for encoding documents in machine-readable form. It is defined in the XML 1.0 Specification produced by the W3C, and several other related specifications, all gratis open standards....
or metadata
Metadata
The term metadata is an ambiguous term which is used for two fundamentally different concepts . Although the expression "data about data" is often used, it does not apply to both in the same way. Structural metadata, the design and specification of data structures, cannot be about data, because at...
definitions. Refactoring the above example to use an external XML-definition framework:
In this case, a dependency injection service is used to retrieve an instance of
a class that implements the
IAutomatedStockTrader
contract. From the configuration file the DependencyManager determines that it must create an instance of the VerySimpleStockTraderImpl class. By examining the constructor arguments via reflection, the DependencyManager further determines that the VerySimpleStockTraderImpl class has two dependencies; so it creates instances of the IStockAnalysisService and IOnlineBrokerageService, and supplies those dependencies as constructor arguments.As there are many ways to implement dependency injection, only a small subset of examples are shown here. Dependencies can be registered, bound, located, externally injected, etc., by many different means. Hence, moving dependency management from one module to another can be accomplished in many ways.
Unit testing using injected mock implementations
Executing the code given above against a live brokerage service might have disastrous consequences. Dependency injection can be used to subsitute test implementations in order to simplify unit testing. In the example given below, the unit test registers replacement implementations of the IOnlineBrokerageService and IStockAnalysisService in order to perform tests, and validate the behavior of VerySimpleStockTraderImpl.Benefits
One benefit of using the dependency injection approach is the reduction of boilerplate code in the application objects since all work to initialize or set up dependencies is handled by a provider component.Another benefit is that it offers configuration flexibility because alternative implementations of a given service can be used without recompiling code. This is useful in unit testing, as it is easy to inject a fake implementation
Mock object
In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the...
of a service into the object being tested by changing the configuration file, or overriding component registrations at run-time.
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:- 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 that the framework uses to inject the dependency.
- Type 3 or constructor injection, in which the dependencies are provided through the class constructor.
It is possible for other frameworks to have other types of injection, beyond those presented above.
See also
- Architecture description languageArchitecture description languageDifferent communities use the term architecture description language. Some important communities are the system engineering community, the software engineering community and the enterprise modelling and engineering community...
- Strategy patternStrategy patternIn computer programming, the strategy pattern is a particular software design pattern, whereby algorithms can be selected at runtime. Formally speaking, the strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable...
- Plug-in (computing)
- Inversion of controlInversion of ControlIn software engineering, Inversion of Control is an abstract principle describing an aspect of some software architecture designs in which the flow of control of a system is inverted in comparison to procedural programming....
External links
- A beginners guide to Dependency Injection
- Dependency Injection & Testable Objects: Designing loosely coupled and testable objects - Jeremy Weiskotten; Dr. Dobb's JournalDr. Dobb's JournalDr. Dobb's Journal was a monthly journal published in the United States by CMP Technology. It covered topics aimed at computer programmers. DDJ was the first regular periodical focused on microcomputer software, rather than hardware. It later became a monthly section within the periodical...
, May 2006. - Design Patterns: Dependency Injection -- MSDN Magazine, September 2005
- DI cartoon
- Martin Fowler's original article that introduced the term Dependency Injection
- P of EAA: Plugin
- The Rich Engineering Heritage Behind Dependency Injection - Andrew McVeigh - A detailed history of dependency injection.
- What is Dependency Injection? - An alternative explanation - Jakob Jenkov
- Writing More Testable Code with Dependency Injection -- Developer.com, October 2006
- Managed Extensibility Framework Overview -- MSDN
- Old fashioned description of the Dependency Mechanism by Hunt 1998