Dependency Injection
In designing an
object-oriented application, a major principal of design is "loose
coupling". Loosely, not meant for the pun, "loose coupling"
means that objects should only have as many dependencies as is needed to do
their job Loose coupling promotes greater reusability, easier maintainability,
and allows you to easily provide "mock" objects in place of expensive
services, such as a socket-communicator.
"Dependency
Injection" (DI), also more cryptically known as "Inversion of
Control" (IoC), can be used as a technique for encouraging this loose
coupling.
There are two primary
approaches to implementing DI:
- Constructor injection
- Setter injection.
Constructor Injection
Constructor Injection is
the DI technique of passing an object's dependencies to its constructor.
The below example includes
a class,
Customer
, that
exposes a method for retrieving every sales-order that the customer made on a
particular date.
Consequently, the
Customer
class needs a data-access
object for communicating with the database.
Assume, an
OrderDao
("order data-access
object") exists which implements the interface IOrderDao
.One way that a Customer
object can get this dependency
is by executing the following within the: IOrderDao
orderDao = new OrderDao();
.
The primary disadvantage of
this is two-fold:
- the benefit of having the interface in the first place has been negated since the concrete instance was created locally, and
OrderDao
cannot easily be replaced by a mock object for testing purposes. (Mock objects will be discussed shortly.)
The aforementioned example
follows:
public class Customer
{
public Customer(IOrderDao orderDao)
{
if (orderDao == null)
throw new ArgumentNullException("orderDao may not be null");
this.orderDao = orderDao;
}
public IList GetOrdersPlacedOn(DateTime date)
{
// ... code that uses the orderDao member
// get orders from the datasource ...
}
private IOrderDao orderDao;
}
In the example, note that the
constructor accepts an interface; it does not accept a concrete object.
Also, note that an
exception is thrown if the
orderDao
parameter is null
.
This emphasizes the
importance of receiving a valid dependency.
Constructor Injection is,
in my opinion, the preferred mechanism for giving an object its dependencies.
It is clear to the developer invoking the object which dependencies need to be
given to the
Customer
object for proper execution.
But consider the following
example... Suppose you have a class with ten methods that have no dependencies,
but you're adding a new method that does have a dependency on
IOrderDao
. You could change the
constructor to use Constructor Injection, but this may force you to change
constructor calls all over the place.
Alternatively, you could
just add a new constructor that takes the dependency, but then how does a
developer easily know when to use one constructor over the other.
Setter Injection
Setter Injection does not
force dependencies to be passed to the constructor. Instead, the dependencies
are set onto public properties exposed by the object in need. As implied
previously, the primary motivators for doing this include:
- supporting dependency injection without having to modify the constructor of a legacy class, and
- allowing expensive resources or services to be created as late as possible and only when needed.
The code below modifies the
Constructor Injection example to use Setter Injection instead:
public class Customer
{
public Customer()
{
}
public IOrderDao OrderDao
{
set {
orderDao = value;
}
get
{
if (orderDao == null)
throw new MemberAccessException("orderDao" +
" has not been initialized");
return orderDao;
}
}
public IList GetOrdersPlacedOn(DateTime date) {
//... code that uses the OrderDao public
//... property to get orders from the datasource ...
}
// Should not be called directly;
// use the public property instead
private IOrderDao orderDao;
}
In the above example, the
constructor accepts no arguments. Instead, the invoking object is responsible
for setting the
IOrderDao
dependency before the method GetOrdersPlacedOn
is called.
With Constructor Injection,
an exception is thrown if the dependency is not set immediately, i.e., upon
creation.
With Setter Injection, an
exception isn't thrown until a method actually attempts to use the dependency.
Make note of the fact that
GetOrdersPlacedOn
uses the public OrderDao
property; it does not
call the private orderDao
directly. This is so that the getter method has an opportunity to validate if
the dependency has yet been initialized.
Setter Injection should be
used sparingly in place of Constructor Injection, because it:
- does not make it clear to the developer which dependencies are needed when, at least until a "has not been initialized" exception is thrown, and
- makes it a bit more difficult to track down where the exception came from and why it got thrown. With this said, Setter Injection can save on modifying a lot of legacy code when introducing new methods, and can provide a performance boost if the dependency is expensive or not easily accessible.
The Injectors
The next logical question
is, what actually creates the dependencies that are to be injected into
"injectees"?
There are two appropriate
places for adding creation logic: controllers and containers.
DI Controllers
The "DI
controller" approach is the simpler to understand and implement. In a
properly tiered architecture, an application has distinct layers for handling
logic.
The simplest layering
usually consists of a data-layer for talking to the database,
a presentation-layer for
displaying the UI, and a domain-logic layer for performing business logic. A
"controller" layer always exists, even if not well defined, for
coordinating UI events to the domain and data layers, and vice versa.
For example, in ASP.NET,
the code-behind page acts as a rudimentary controller layer. More formalized
controller-layer approaches exist: Struts and Spring for Java; Front Controller
and Spring .NET for .NET. All of these approaches follow some form of variant
of the Model-View-Controller pattern. Regardless of what you use as your
controller, the controller is an appropriate location for performing Dependency
Injection "wiring". This is where concrete objects are created and
injected as dependencies.
What follows are two
examples of DI performed by a controller. The first is an illustrative example
of "production code" - code that you'd end up deploying. The second
is an example of "test code" - code that's used to test the
application, but is not deployed and does not have the need to have a live
database.
Controller code performing
the dependency injection (e.g., from an ASP.NET code-behind page):
//... code performed when the controller is loaded ...
IOrderDao orderDao = new OrderDao();
// Using Setter Injection on a pre-existing customer
someCustomer.OrderDao = orderDao;
IList ordersPlacedToday =
someCustomer.GetOrdersPlacedOn(DateTime.Now);
...
Unit-test code performing
dependency injection:
IOrderDao orderDao = new MockOrderDao();
// Using Setter Injection on a pre-existing customer
someCustomer.OrderDao = orderDao;
IList ordersPlacedToday = someCustomer.GetOrdersPlacedOn(DateTime.Now);
One of the major benefits
of using a DI-controller to inject dependencies is that it's straightforward
and easy to point to where the creation is occurring.
The drawback to using
DI-controllers is that the dependencies are still hard-coded somewhere; albeit,
they're hard-coded in a location that is often subject to frequent changes
anyway.
Another drawback is that
now the DI-controllers themselves can't be easily unit-tested with mock
objects.
In ASP.NET, I prefer to use
the Model-View-Presenter (MVP) pattern, and have the ASP.NET code-behind page
create dependencies and inject them to the presenter via Construction
Injection. Additionally, I use UserControls as the View part of the pattern, so
the ASP.NET code-behind acts purely as an MVP "dependency
initializer" between the UserControls (View) and their presenters.
Another option to
implementing constructor or setter DI is the use of an application container...
DI Containers
Inversion-of-Control/Dependency-Injection
"containers" can be used to watch an application and inject
dependencies whenever a particular event occurs.
For example, whenever a
Customer
instance is created, it
automatically gets injected with its needed dependencies.
It's a strange concept at
first, but can be useful for managing large applications with many service
dependencies. Different container providers each have their own mechanism for
managing dependency injection settings.
Spring .NET allows you to
define dependency injections within an XML file. The following example Spring
.NET XML uses Setter Injection to give an ASPX code-behind page its data-access
object dependency:
<spring>
<context type="Spring.Context.Support.WebApplicationContext, Spring.Web">
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<!-- Data Access Object that will be injected into ASPX page -->
<object id="daoFactory" type="MyApp.Data.DaoFactory, MyApp.Data" />
<object type="ViewDetails.aspx">
<property name="DaoFactory" ref="daoFactory" />
</object>
</objects>
</spring>
The ASPX code-behind simply
exposes a public property called
DaoFactory
that "catches" the dependency whenever the page gets called. Unit Testing with Injected Mock Objects
The greatest benefits that
I've experienced using DI are cleaner unit testing and greater portability.
Portability is a given as most of the dependencies are on interfaces. But let's
look at how DI can benefit unit testing as well. It is often the case that
developers write unit tests against a live database. This is all well and fine,
but it quickly brings the speed of a suite of unit tests to a crawl. To keep
unit testing from becoming a drag, it needs to be easy to turn off slow unit
tests that actually hit the database while still testing the business logic
that depends on data access. Mock objects are perfect for doing just this. A
mock object simulates the responses of an actual object, acting as if it's
using a real resource. They're great for mocking access to a database, mocking
calls to IO, mocking calls to a web service, etc.
The below code shows a mock
data-access implementation of the
IOrderDao
interface used throughout the article:public class MockOrderDao : IOrderDao {
public Order GetOrderById(long orderId) {
Order foundOrder = new Order();
foundOrder.SaleDate = "1/1/06";
foundOrder.ID = orderId;
return foundOrder;
}
}
This trivial, mock
data-access object implements
IOrderDao
,
and can therefore be passed to any object that's dependent on IOrderDao
via Constructor or Setter
injection. Now all your business logic can be tested without actually hitting
the database. In my own test suite, I usually have a section that contains
"real" database tests, and then pass mock database-access objects for
testing my domain objects.
refered from Code Project.com