Background: We’ve started a weekly patterns & practices meeting at work with some of our senior developers where our discussions and actions will hopefully bring some improvement to the current development environment. Once a week one of us has an opportunity to showcase a new topic – much akin to knowledge transfer session but more fine-grained and at a higher level than the technology. Gareth Stephenson suggested we blog about the content for the benefit of others, which I think is not a bad idea at all. Furthermore, I’ve never been able to find a good, down-to-earth resource that explains the benefits of DI and how to get started with it. This post is a recording of a short presentation I performed on DI and the patterns that emerge in its usage – I hope it proves useful.
.NET Developers don’t do patterns
There’s a surprising amount of .NET developers that are still not familiar with Dependency Injection (DI), and patterns in general. In the Java world, File –> New Project would give you a template with Spring baked in – when working with .NET that kind of guidance is missing from the toolset. Perhaps some exposure to MEF with .NET 4.0 will entice .NET developers to look further than the Microsoft toolset, in the same way that Entity Framework exposed the majority of developers to ORM’s with NHibernate getting a bit of adoption as a result. DI and Inversion of Control go hand-in-hand, but be careful not to confuse the two – DI is a tool, whereas Inversion of Control is a pattern.
A World filled with legacy code
We’ll get to why we would use a container soon, but consider the following code in the meanwhile:
public class OrderPlacementService {
public void Process(Order order) {
// For a big order, notify somebody important
if (order.Amount > 500) {
SmsSender.SendSms("0113334444", "Pretty big order received.");
}
// Actually process the order
Console.WriteLine("Order processed.");
}
}
public static class SmsSender
{
public static void SendSms(string number, string text)
{
Console.WriteLine("Sent sms \"{0}\" to number \"{1}\".", text, number);
}
}
The important parts that denote processing represent themselves as writes to standard output. This code is typical of legacy systems, and even some systems currently under development. It suffers from the following problems related to coupling and cohesion :
- Static classes and calls to static members increase the coupling between classes and makes the code harder to change, as well as reduce testability.
- Violation of the Open/Closed Principle (pdf) forces us to change the service when we want to change the notification functionality.
- Violation of the Single Responsibility Principle (pdf) by handling order processing, business rules and notifications makes the service more fragile by introducing reasons for change.
- Calls to concrete classes and violation of the Dependency Inversion principle (pdf) maximizes coupling between completely unrelated pieces of code.
- Resembles procedural programming making it difficult to plug code in and out without affecting anything else.
- The above makes the code inherently untestable – we cannot test the code without sending an SMS. Keeping in mind that we would wish to run our tests as frequently as possible, a test that costs money is not the best idea.
Using a DI framework changes the way you code by solving the problem of constructing objects and the provision of dependencies – it makes it possible to decouple your implementations from one another, thereby improving your code by making it more amendable to testing and more amendable to change. Before we introduce a DI framework to the sample code above, we need to make some improvements to it.
Improving the existing code
We start off by removing any static calls by making SendSms an instance member. This will allow us to replace SmsSender with a test object (test double, mock, or a stub) to test it.
public interface ISmsSender
{
void SendSms(string number, string text);
}
public class SmsSender : ISmsSender
{
public void SendSms(string number, string text)
{
Console.WriteLine("Sent sms \"{0}\" to number \"{1}\".", text, number);
}
}
We can then write the OrderService class in a cleaner way to reduce the dependency on a concrete implementation of an SMS sender:
public class OrderPlacementService {
private readonly ISmsSender smsSender;
public OrderPlacementService(ISmsSender smsSender)
{
this.smsSender = smsSender;
}
public void Process(Order order) {
// For a big order, notify somebody important
if (order.Amount > 500)
{
smsSender.SendSms("0113334444", "Pretty big order received.");
}
// Actually process the order
Console.WriteLine("Order processed.");
}
}
Notice the effects this simple change has:
- We’re using inversion of control to “push” the dependency to the caller, this relieving ourselves of responsibility of it.
- Our OrderPlacementService now explicitly states the dependencies that it needs to function properly by requiring an ISmsSender as a constructor parameter.
- Since we’re not interested in the concrete implementation of an SmsSender, we’ve replaced the concrete dependency with an interface. We care about the What (contract), not the Who (concrete type) or the How (actual implementation details).
A Sample implementation
The calling code must now provide the dependency to the OrderService. For example :
static void Main(string[] args)
{
var orderPlacementService = new OrderPlacementService(new SmsSender());
Console.WriteLine("Placing small order...");
orderPlacementService.Process(new Order { Amount = 200 });
Console.WriteLine("Placing big order...");
orderPlacementService.Process(new Order { Amount = 700 });
Console.WriteLine();
Console.WriteLine("Done.");
Console.ReadLine();
}
Running the code above provides the following output:
Providing extension points
We can improve the code even more by abstracting at a slightly higher level, and removing the knowledge of the order amount condition (greater than 500) and the resulting action (sending an SMS) from the service by introducing an extension point and moving the logic to a custom extension :
public interface IInterceptor<T>
{
void Intercept(T item);
}
public interface IOrderInterceptor : IInterceptor<Order>
{
}
public class LargeOrderInterceptor : IOrderInterceptor
{
private readonly ISmsSender smsSender;
public LargeOrderInterceptor()
: this(new SmsSender())
{
}
public LargeOrderInterceptor(ISmsSender smsSender)
{
this.smsSender = smsSender;
}
public void Intercept(Order order)
{
if (order.Amount > 500)
{
smsSender.SendSms("0113334444", "Pretty big order received.");
}
}
}
With the interceptor interface in place, our code for our OrderService is greatly simplified :
public class OrderPlacementService : IOrderPlacementService {
private readonly IOrderInterceptor orderInterceptor;
public OrderPlacementService(IOrderInterceptor orderInterceptor)
{
this.orderInterceptor = orderInterceptor;
}
public void Process(Order order)
{
orderInterceptor.Intercept(order);
// Actually process the order
Console.WriteLine("Order processed.");
}
}
Enough dependencies to go around
Our calling program needs to provide the appropriate dependencies :
var orderPlacementService = new OrderPlacementService(new LargeOrderInterceptor(new SmsSender()));
and now another problem emerges. Firstly, it’s a real pain to have to provide the dependencies for OrderPlacementService (and it’s dependencies) by hand. The bigger problem here is that we’ll be violating DRY if the service gets used in more than one place, which it’s bound to be. Coupling increases since every object that has a dependency on OrderPlacementService would have a direct dependency on the LargeOrderInterceptor and SmsSender classes, which denotes a violation of the Single Responsibility Principle. The classic solution for this problem is to make use of the Factory pattern in order for this knowledge to be in one place (and thus changed with ease if the need arises):
public interface IServiceFactory
{
IOrderPlacementService GetOrderPlacementService();
}
public class ServiceFactory : IServiceFactory
{
public IOrderPlacementService GetOrderPlacementService()
{
return new OrderPlacementService(new LargeOrderInterceptor(new SmsSender()));
}
}
##Factories Are Not The Solution
Using the Factory pattern in this case has it’s own sets of problems :
- Dependency Replacement : While we got rid of the dependency on LargeOrderInterceptor and SmsSender, we’ve replaced it with a dependency on ServiceFactory. ServiceFactory then becomes difficult to maintain as any changes to it affects a big chunk of the codebase.
- Proliferation of helper classes : Splitting the ServiceFactory into smaller factories, each responsible for constructing a different object removes the hotspot, but brings to life a multitude of redundant helper classes (classes that have no real identity or well-defined purpose).
Another technique called Poor Man’s Dependency Injection can reduce the spread of dependency knowledge by one level by creating a default constructor that injects the dependencies into a more specialized constructor. Applying this technique to the LargeOrderInterceptor would yield the following constructors :
private readonly ISmsSender smsSender;
public LargeOrderInterceptor() : this(new SmsSender())
{
}
public LargeOrderInterceptor(ISmsSender smsSender)
{
this.smsSender = smsSender;
}
Poor Man’s Dependency Injection also isn’t helpful in this regard, but it may be useful in constrained environments to increase the testability of code.
Dependency injection to the rescue
This is the situation where DI is helpful – it takes on the knowledge of construction and stores it in a central place, removing duplication and making it easy to change. It provides the glue that holds an application together and allows us to create small classes isolated from each other, but working together to achieve a common goal. You can think of a container as a simple hash table of types with interfaces mapping to concrete types. If we ask the container for a type implementing IFoo, it can construct and return a Foo concrete instance. If Foo needs any dependencies, the container will construct and inject those as well. It will construct an entire object graph without your code having any knowledge on the concrete types or the dependencies they take.
We’ll use StructureMap (one of many containers available for .NET) to illustrate. First, we need to add some code to initialize the container. Typically, this bootstrapping code gets run whenever your application starts up in some executable bootstrapper. Initialization of the container should add set up the container for construction of any objects that we might need during the execution of our program. StructureMap’s fluent API makes this an easy task :
StructureMap.ObjectFactory.Initialize( x =>
{
x.For<ISmsSender>().Use<SmsSender>();
x.For<IOrderInterceptor>().Use<LargeOrderInterceptor>();
x.For<IOrderPlacementService>().Use<OrderPlacementService>();
});
The code above is registering a type with the container. Now, whenever we need an OrderPlacementService, we ask the container to construct it for us using the Service Locator pattern :
StructureMap.ObjectFactory.GetInstance<IOrderPlacementService>();
And magic! We have an usable IOrderPlacementService:.
In the background StructureMap matched the requested interface to a concrete type, found the greediest constructor (the one that has the most parameters), and injected the IOrderInterceptor dependency into it. The same happened to the ISmsSender dependency for the interceptor. Our code just ran without any compile time knowledge on concrete implementations for its dependencies. We achieved both coding to interface (ignorance on concrete implementations) that reduces coupling, and solved the problem of object construction without being inappropriately intimate with them.
Configuring all your dependencies can cause blindness
Coding using all the practices discussed earlier can result in a lot of types (and registrations) in big projects - having to update the container configuration every time you add a class can be a real pain. To counter this, most container implementations provide a way of Auto-Registration - scanning types in assemblies and registering the m automatically using certain conventions.
With StructureMap, we can instruct the container to scan on initialization :
ObjectFactory.Initialize( x => x.Scan(scanner =>
{
scanner.AssembliesFromPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
scanner.WithDefaultConventions();
scanner.LookForRegistries();
}));
This little snippet above has the following effects :
- Let StructureMap scan all the assemblies in the application’s home directory.
- Register types that match the default conventions – that is, if StructureMap finds an interface IFoo and a concrete implementation called Foo, it will automatically register the types. Note that we can be customize these conventions.
- LookForRegistries finds and adds the information contained in registries. Registries allow us to split the registration configuration in scope (for example on a namespace or assembly level).
Note that scanning, in our case, removed the need for us to explicitly register ISmsSender and IOrderService since the naming of these classes match the default conventions. Our interceptor is not so lucky, and has to be manually registered using a registry :
public class DomainRegistry : Registry
{
public DomainRegistry()
{
For<IOrderInterceptor>().Use<LargeOrderInterceptor>();
}
}
## Containers Accommodate Change
We might realise that we have the need for more interceptors to run whenever an order comes in. We can solve this problem by using the Composite pattern, which allows us to treat a collection of items as a single item in the following way :
public interface ICompositeOrderInterceptor : IInterceptor<order>
{
IList<IOrderInterceptor> Items { get; }
}
public class CompositeOrderInterceptor : ICompositeOrderInterceptor
{
public CompositeOrderInterceptor(params IOrderInterceptor[] items)
{
Items = items;
}
public void Intercept(Order order)
{
foreach (var interceptor in Items)
{
interceptor.Intercept(order);
}
}
public IList<IOrderInterceptor> Items { get; private set; }
}
With this in place, we can construct our container to add all instances of IOrderInterceptor and inject the composite wherever it’s used. Their are two distinct ways of registering types in an container. Which one to use depends on the usage and number of items of the individual types:
- We can request the default implementation for an interface using the Service Locator pattern. Asking for an IFoo implementation will always result in a Foo instance with no further information.
- We can request a list of implementations when multiple concrete implementations back an interface. A request for IFoo implementations will result in a list of instances of every type that implements IFoo.
Adding
scanner.AddAllTypesOf<IOrderInterceptor>();
to our scanning and removing the default registration of LargeOrderInterceptor will register all instances implementing IOrderInterceptor. As a result we’ll be able to ask the container for all instances IOrderInterceptor and get back a list of instances of all the concrete implementations found. If we replace the dependency of IOrderInterceptor with ICompositeOrderInterceptor in our order service, we can effectively add functionality to our order service without changing a line of code in it or touching the container configuration again.
private readonly ICompositeOrderInterceptor orderInterceptor;
public OrderPlacementService(ICompositeOrderInterceptor orderInterceptor)
{
this.orderInterceptor = orderInterceptor;
}
Our OrderPlacementService will not receive a composite interceptor with the known interceptors injected into it :
We’re taking advantage of a specific feature of StructureMap here : if you require a list or an array of items in your constructor, the container will provide all instances registered in it. If we add another interceptor to trigger on small orders,
public class SmallOrderInterceptor : IOrderInterceptor
{
public void Intercept(Order order)
{
if (order.Amount > 300)
{
Console.WriteLine("Small order!");
}
}
}
the container will automatically inject an instance of the new interceptor :
Which allows us to change the behaviour of our application by simply adding code and not modifying anything. See Jimmy Bogard’s post on connecting implementations to open generics types for a more advanced example on how containers can accommodate change in an application.
Different types of injection
Two very distinct styles of injection are Constructor Injection and Setter Injection.
Constructor injection takes dependencies using the constructor like the code above – it involves setting global variables (if necessary) to the constructor parameters to make these dependencies available for the entirety of the class. The constructor advertises dependencies as required – you can not instantiate an object without providing it’s dependencies.
Setter Injection provides properties for setting dependencies. No explicit advertising of dependencies is necessary and you can instantiate a class without the required dependencies – so a set of reasonable defaults should be assumed. This does make a class easier to instantiate, but adds the onus on the class to manage it’s default dependencies.
Constructor injection is clearly preferred over Setter Injection in most cases. Where you can’t control the instantiation of objects (as in Web Forms), you can apply Setter Injection to the already existing object.
You can do DI wrong
There are several anti-patterns you can get yourself into : Relying on the Service Locator pattern effectively replaces any dependencies with a dependency on the container, defeating the purpose just a little bit. Not only could the choice of container percolate throughout our codebase, but now any tests need to mock out the container. Even worse is a direct dependency on a static container like ObjectFactory since we can’t run tests without some shared state between them. In some cases this querying the container direct is a necessary evil, particularly when using the container as a plugin framework. This is by far the most common anti-pattern I see with people just getting started in DI.
Containers are best called only on application initialization and resolving root components with the rest of the codebase not having any explicit dependency on it. Too many dependencies tied to a class can cripple a code base, making it fragile and prone to change. While the problem is not tied directly to DI, DI can help hide the pain of having too many dependencies (not a good thing). Using Setter injection enables objects to be instantiated in an invalid state. It also manages to tie classes directly to their dependencies through the need for reasonable defaults.
But wait! There’s more
Dependency Injection containers have grown into much more than simple type resolvers. Once you’re comfortable with the basics you can start investigating:
- Containers typically provide some kind of lifecycle management for constructed objects. For example, it’s dead easy to configure a dependency as a singleton, or to tie it to the HttpContext.
- Conditional object construction.
- Custom conventions for dependency registration.
- Connecting implementations to open generic types.
- Nested containers.
- Interception and substitution.
A container implementation for everyone
There are several open source/freely available containers for use on .NET. These are the most popular ones :
- StructureMap: the oldest one, and still my favourite. Combines power with speed and an elegant Fluent API.
- Castle Project: container is part of a suite of tools. Powerful, but I’ve found the syntax rather hard to get started with.
- Spring.NET: not just a container, but an application framework. Very heavy on XML configuration, but the documentation is good and enterprise support is always welcome.
- Ninject: lightweight container that embraces lambdas to it’s fullest.
- AutoFac
Some people even write their own.
Conclusion
Probably the most challenging aspect of explaining DI is the overwhelming amount of information that comes along with it. In the last couple of years I’ve come to the conclusion that this is the way of building applications using statically typed languages – of course, most of the principles apply to dynamic languages, but IOC just isn’t that big in, for example, Ruby.
Photo by Ilya Pavlov on Unsplash