Dependency Injection (DI) is a way of implementing the general Inversion of Control (IoC) -pattern. We do this to make our classes independent of each other. Combined with a preference for programming against interfaces instead of directly referencing the implementing classes we can achieve what is called loose coupling. The implementation details of one class have minimal impact on the innards classes using it. Additionaly using Dependency Injection makes your classes easier to unit-test, as they expose their dependencies externally and you can pass in mocks and stubs instead of full implementations.
It is easier to understand the pattern by looking at code-examples. We're creating code for a coffee-shop, and we're looking at the implemetation of a barista (the scrawny, wierd guy who makes the coffee). In my examples I will not show the interfaces, and just assume that all methods are defined in them.
Our barista has one method, where he uses his experience, a nice, big coffe-maker and some extras to concoct a potion:
1: public class Barista : IBarista {
2: private const int _yearsOfExperience = 5;
3: private IEducation _major = new LiberalArtsMajor();
4:
5: public IBeverage MakeMeAn(IOrder order) {
6: var knowledge = new Experience(_yearsOfExperience, _major);
7: var plan = knowledge.HowDoIMake(order);
8:
9: var coffeeMaker = new BigAstra();
10:
11: var beverage = coffeeMaker.MakeBeverageAccordingTo(plan);
12:
13: var otherStuff = new AssortedCreamsAndSyrups();
14:
15: var additives = knowledge.WhatShouldIAddAccordingTo(plan);
16: beverage.Add(additives);
17:
18: return beverage;
19: }
20: }
This piece of code is pretty readable, so it's not the worst code in the world. However, it is tighlty coupled to several other classes. Firstly it binds to the LiberalArtsMajor -class (which is apt, but still not a good thing) to set its private field _major
. Inside the method itself it binds to the classes Experience
, BigAstra
and AssortedCreamsAndSyrups
. All of these object have to be created for the Barista to make a lovely, warm, black and brown elixir of wakefullnes. Thus these direct couplings make our Barista brittle.
Experience tells us that our classes should be as small and simple as possible, and that they should have one and only one reason to change. It is not far-fetched to imagine the BigAstra
class acquiring constructor parameters requiring the caller to provide a source of water, a source of power and a repository of coffee-beans (as this fits neatly with what it would need in real-life).
1: public class BigAstra : ICoffeeMaker {
2: public BigAstra(IWaterSource water, IPowerSourceAt220Hz power, ICoffeeRepository godlyBeans) {
3: // something very clever and difficult...
4: }
5: // the rest is left as an exercise for the reader
6: }
This change means that we will have to change the implementation of our barista because the implementation of the coffee-maker he is using changed. Furthermore, this change in how you make a BigAstra
coffe-maker will cascade into all the places where the BigAstra
is used. The same is true for all the other classes used by our Barista and in fact all the classes in the system. Good tools can certainly help us deal with this to a certain extent, but we quickly find ourselves in what I call combinatorial hell. Our barista has too many reasons to change - he is too tightly coupled to what he uses. How can we improve on this?
One solution to this problem I've seen used is to forgo object-orientation altogether and make the classes into a simple collection of static methods. By doing this we are binding ourselves tightly to one implementation, and refusing ourselves powerful object-oriented techniques like inheritance and polymorphism. In essence we are creating a Singleton implementation of the object we need. This technique transports us back into imperative programming in a fell swoop. Please, please do not do this! If you want to read more about why this is a bad idea I recomment Steve Yegge's "Singleton Considered Stupid" -post, and for a shorter view you can read Rasmussen's answer on StackOverflow. Seriously, kittens die and hamsters get palsy when we do this.
How can we solve our combinatorial problem without throwing 30 years of object-oriented goodness on the pyre? By reversing the control through dependency injection, that's how (how's that for a tagline?). This is not a new technique, in fact you've already seen it in this post. When our BigAstra
changed its constructor to expose what it needed (its dependencies) it enabled Dependency Injection. There are several flavours of Dependency Injection, but common to them all is that they expose what the class depends on to the outside, allowing its behaviour to be modified without changing its innards. This is also known as the open / closed principle, which says the class should be open to extension but closed to modification. By exposing its dependencies BigAstra
is also explicitly signalling that while it needs these things to function it does not accept responsibility for creating them (which follows from the single responsibility principle).
Some ways to acheive DI are to expose the dependencies using a Factory -pattern, expose them through properties / public fields (property injection), require them as constructor parameters (constructor injection) or we could use a Service Locator to build them up runtime (I'm sure there are other ways that slip my mind right now).
I strongly prefer using constructor injection, and also exposing any dependencies that have to change run-time to property injection. When I set up the object-graph I prefer to use a Service Locator which lets me declare how to construct objects when needed and call this when required. In the best of all possible worlds this should be exactly once in every application's lifetime.
We are going to change our barista to support DI by pulling all of his dependencies out into the class and exposing them through the constructor. The coffee-maker and the condiments may need to be replaced in the barista's lifetime (though baristas tend to seem quite frail, and cooffee-machines always look strong and shiny), so we will expose these to property injection as well as constructor injection.
1: public class Barista : IBarista {
2: private const int _yearsOfExperience = 5;
3: private IEducation _major;
4: private IExperience _attitude;
5: public ICoffeeMachine ShinyThing { get; set; } // Property Injection
6: public ICondiments Flourish { get; set; } // Property Injection
7:
8: // Constructor Injection
9: public Barista(IEducation educatedAs, IExperience relevantExperience,
10: ICoffeeMachine toolOfTrade, ICondiments sugarAndSpice) {
11: _major = educatedAs;
12: _attitude = relevantExperience;
13: ShinyThing = toolOfTrade;
14: Flourish = sugarAndSpice;
15: }
16:
17: public IBeverage MakeMeAn(IOrder order) {
18: var plan = _attitude.HowDoIMake(order);
19: var beverage = ShinyThing.MakeBeverageAccordingTo(plan);
20: var additives = Flourish.WhatShouldIAddAccordingTo(plan);
21: beverage.Add(additives);
22:
23: return beverage;
24: }
25: }
It is now apparent what our barista depends upon by just looking at the constructor's parameters. Actually, there's something a bit off here. Is the barista really dependent upon his education? Isn't he merely using it as a part of his experience? When we explicitly expose our dependencies like this we'll often find such overlapping (and even conflicting) dependencies. This is a good opportunity for some cleanup, and we end up with a slightly cleaner barista as follows:
1: public class Barista : IBarista {
2: private IExperience _attitude;
3: public ICoffeeMachine ShinyThing { get; set; }
4: public ICondiments Flourish { get; set; }
5:
6: public Barista(IExperience questionableExperience, ICoffeeMachine toolOfTrade,
7: ICondiments sugarAndSpice) {
8: _attitude = questionableExperience;
9: ShinyThing = toolOfTrade;
10: Flourish = sugarAndSpice;
11: }
12:
13: public IBeverage MakeMeAn(IOrder order) {
14: var plan = _attitude.HowDoIMake(order);
15: var beverage = ShinyThing.MakeBeverageAccordingTo(plan);
16: var additives = Flourish.WhatShouldIAddAccordingTo(plan);
17: beverage.Add(additives);
18:
19: return beverage;
20: }
21: }
We are happy with this - our barista is ready to have his dependencies set on creation, two of them can even be changed run-time (this is not thread-safe). The barista has no knowledge of how to set up a coffee-machine, how to get the condiments or how to make the experience he got. He doesn't know or care about you or your band, he just makes killer Cortados.
Haven't we just moved our problem, though? Mustn't whosoever calls the barista now need to know how to set up all the barista's dependencies? In our case the barista is used by a poor, underpaid Cashier. She used to look like this:
1: public class Cashier : ICashier {
2: public IBeverage GetDrinkForCustomer(ICustomer currentCustomer){
3: var order = ConstructAnOrderFrom(currenCustomer.Demands);
4:
5: var jerk = new Barista();
6: var drink = jerk.MakeMeAn(order);
7:
8: return drink;
9: }
10: }
After we've meddled with the Barista's constructor to appease our false gods of object-orientation she now has to look like this abomination?
1: public class Cashier : ICashier {
2: private const int _yearsOfExperience = 5;
3:
4: public IBeverage GetDrinkForCustomer(ICustomer currentCustomer){
5: var order = ConstructAnOrderFrom(currenCustomer.Demands);
6:
7: var experience = new Experience(_yearsOfExperience,
8: new LiberalArtsMajor());
9:
10: /probably has some municipality and plumber dependencies
11: var waterTap = new WaterTap(...);
12: //probably some municipality and electrician dependencies
13: var powerSource = new PowerSource(...);
14: //probably depenent on suppliers and service getting container from stock
15: var coffeeBeans = new CoffeStore(...);
16: var coffeMakerForJerk = new BigAstra(waterTap,
17: powerSource,
18: coffeeBeans);
19:
20: // dependent on suppliers I guess?
21: var condiments = new AssortedCreamsAndSyrups(...);
22:
23: var jerk = new Barista(experience,
24: coffeMakerForJerk,
25: condiments);
26: var drink = jerk.MakeMeAn(order);
27:
28: return drink;
29: }
30: }
Ouch! The answer to this is obviously to push the dependencies of our cashier into her constructor, making the next level up the chain responsible for the instantiation. But this seems like a reductio-ad-absurdum, as we'll have to have a god-class on top setting up our entire object-graph. That can't be good, can it? Well, yes and no. What we want is a Service Locator, or in our case an IoC container. I like StructureMap, as it's easy to work with and lets me define in a declarative way how it should work. It is one of the oldest of its kind in the .Net world, and time-proven.
To get StructureMap's help we have to tell it how to make the objects we are going to require. StructureMap has the concept of a Registry
, which lets us declare what to do when asked for types in a declarative way, like "When asked for A you should return B". StructureMap is smart enough to inspect the constructors of the Bs it can return, and if the constructor requires something it knows how to make it puts that into it. This works well as long as you have defined all the types you need (the configuration is valid), and there are no circle-dependencies (ie. Foo requires a Bar, which requires a Foo). A registry for our coffee-shop might look like this:
1: public CoffeeShopRegistry : Registry {
2: //could be gotten from a config-file or somesuch
3: private const int _yearsOfExperience = 5;
4:
5: public CoffeShopRegistry() {
6: ForRequestedType<ICoffeeShop>()
7: .TheDefaultIsConcreteType<CoffeShop>();
8: ForRequestedType<ICashier>()
9: .TheDefaultIsConcreteType<Cashier>();
10: ForRequestedType<IBarista>()
11: .TheDefaultIsConcreteType<Barista>();
12:
13: ForRequestedType<IEducation>()
14: .TheDefaultIsConcreteType<LiberalArtsMajor>();
15: ForRequestedType<ICoffeeMaker>()
16: .TheDefaultIsConcreteType<BigAstra>();
17: ForRequestedType<IWaterSource>()
18: .TheDefaultIsConcreteType<WaterTap>();
19: ForRequestedType<IPowerSourceAt220Hz>()
20: .TheDefaultIsConcreteType<MainsLine>();
21: ForRequestedType<ICoffeeRepository>()
22: .TheDefaultIsConcreteType<SomeCoffeStorage>();
23:
24: ForRequestedType<IExperience>()
25: .TheDefault.Is
26: .ConstructedBy(context =>
27: new Experience(_yearsOfExperience,
28: context.GetInstance<IEducation>()));
29: }
30: }
StructureMap won't initialize any objects until asked to, so defining the types our barista depend on after the barista itself is not a problem as long as the internal graph of StructureMap contains all it needs when it is called upon to provide an IBarista. StructureMap can also do other, cool things - like managing the lifetime of the objects, but that's a subject for another post.
We want to kick off StructureMap as one of the very first things in our application, and preferrably we'd only like to call upon it to construct something once (or as few times as possible). A possible Main
would be:
1: public CoffeShopProgram {
2: public static void Main(string[] args) {
3: ObjectFactory.Initialize(context =>
4: context.AddRegistry(new CoffeeShopRegistry()));
5:
6: // Exception if we've missed anything
7: ObjectFactory.AssertConfigurationIsValid();
8:
9: // depends on having a cashier and a barista - MAGIC!
10: var shop = ObjectFactory.GetInstance<ICoffeShop>();
11:
12: shop.Open();
13: while (shop.IsOpen) {
14: var customer = ObjectFactory.GetInstance<ICustomer>();
15: shop.ServeCustomer(customer);
16: }
17: }
18: }
We're setting up ObjectFactory by adding the registry we made, in effect telling it what types it should know about, and how to return something when called upon. We haven't seen any ICoffeeShop implementors, but let's just say that is a class depending on an ICashier for its constructor. StructureMap knows how to make an ICashier - that's an instance of the Cashier -class. But Cashier depends on an IBarista! No problem, StructureMap knows that's a Barista instance, etc.
Implementing Dependency Injection loosens our classes' coupling against other classes, in effect delegating that to somewhere else. The actual calling of constructors is done by our IoC -container, freeing us up to simply declare that we want an IBarista, and assuming one will be created for us. So, instead of needing to know the intimate details of the implementing classes' constructors and dependencies to use an IBarista we just have to ask for one.
And, that's all kinds of cool!