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 demonstrate a new topic – very much akin to knowledge transfer session but more fine-grained and at a higher level than just 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 just the Microsoft toolset, in the same way that Entity Framework finally exposed the majority of developers to ORM’s with NHibernate getting quite 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:
The important parts that denote processing is represented as writes to standard output. This code is typical of legacy systems, and even some systems currently under development. It suffers from several 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 multiple 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 very difficult to plug code in and out without affecting anything else.
- All of 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 completely changes the way you code by solving the problem of constructing objects and the provision of dependencies – it makes it possible to completely 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 simply replace SmsSender with a test object (test double, mock, or a stub) in order to test it.
The OrderService class can then be written in a cleaner way, as to reduce the dependency on a concrete implementation of an SMS sender :
Notice a couple of things about the simple change we made :
- We’re using inversion of control to “push” the dependency to the caller, this relieving ourselves of responsibility of it.
- Our OrderPlacementService now clearly states the dependencies that it needs to function properly by requiring an ISmsSender as a constructor parameter.
- Since we’re not particularly 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 :
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 :
With the interceptor interface in place, our code for our OrderService is greatly simplified :
Enough Dependencies To Go Around
Our calling program needs to provide the appropriate dependencies :
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 is used in in more than one place, which it’s bound to be. Coupling is maximized 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 only be in one place (and thus easily changed if the need comes) :
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 multiple 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 :
Poor Man’s Dependency Injection also isn’t very helpful in this regard – but it can be used in very constrained environments to increase the testability of code.
Dependency Injection To The Rescue
This is exactly 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 is 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 :
This process is known as registering a type with the container. Now, whenever we need an OrderPlacementService, we simply ask the container to construct it for us using the Service Locator pattern :
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. Exactly 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 :
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 StrutureMap finds an interface IFoo and a concrete implementation called Foo, it will automatically register the types. Note that these conventions can be customized.
- 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 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 :
Containers Accommodate Change
We might realise that we have the need for multiple interceptors to run whenever an order comes in. We can solve this problem easily using the Composite pattern, which allows us to treat a collection of items as a single item in the following way :
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 is determined by the usage and number of items of the individual types :
- The default implementation for an interface can be requested using the Service Locator pattern. Asking for an IFoo implementation will always result in a Foo instance if no further information is provided.
- A list of implementations can be requested when an interface is implemented by several concrete implementations. A request for IFoo implementations will result in a list of instances of every type that implements IFoo.
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.
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,
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. Dependencies are advertised as required through the use of the constructor – an object cannot be instantiated without providing it’s dependencies.
Setter Injection on the other hand provides properties for setting dependencies. There is no explicit advertising of needed dependencies and a class can be instantiated without the dependencies being set – 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 WebForms), 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.
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.