I have three kids. They are older now, but when they were younger, it was fun to bake with them. We’d bake a cake or cookies or some other confection that they would then enjoy eating. But there was one problem — we made a huge mess when we baked. There was flour and sugar and all kinds of stuff all over the place and all over us. The process of baking left us with a big job of cleaning up. It made for a lot of work. Sure, we had fun, but the mess was there, nonetheless.
How to avoid the mess? Well, we could be more careful, I suppose, but kids will be kids. Often, we would be baking a birthday cake. An alternative, of course, to all of the hassle and mess of baking a cake would be to go to the store and buy a pre-made cake. An even better solution would be to find a bakery that would deliver birthday cakes to order, dropping off at your doorstep exactly what you wanted for your birthday cake.
Now here is something else to consider. Say the delivery guy shows up and he’s been tromping through mud and his boots are filthy and he’s got a vicious cough. Are you going to let him into your house? No — of course not. You’d probably take one look at him through the crack in the door and tell him to put the cake box down on the porch and get the heck off your lawn. You’d be careful about the cake, too, I guess, but for the sake of argument, we’ll assume it is well wrapped and protected from whatever phlegm the delivery guy was emitting. In other words, you’d want your interaction with the delivery person to be as minimal as possible, but you still want the cake. Your kid is going to be pretty upset if he or she doesn’t get to blow out the candles on their birthday cake. You want the cake without the mess and with the thinnest of interactions with the delivery person.
One might even say that you had a dependency on a birthday cake, and the bakery was injecting that dependency by delivering the cake to you. Hmm.
Or let’s say you are at the supermarket, and you have a grocery cart full of groceries. You take them to the checkout guy, and he rings them all up. He says, “That’ll be $123.45.” So what do you do? Do you hand him your wallet and have him dig around for cash or a credit card? Of course not — you want to keep the interaction as minimal as possible with your wallet — you don’t want him rooting around in it. Who knows what will happen. Instead, you give the guy cash, or you pull out your credit card and hand it to him. Or better yet, you swipe the card yourself so that the checkout guy never even touches your card. Again, you want to have that interaction be as minimal as possible. You might say that you want to keep the interface between you and the clerk to a minimum.
One more. Say you are looking at buying a house. You search all over town, and you finally find a place that you like: good location, excellent school district, good neighborhood. Only there is one problem: All the electrical devices are hardwired into the house. All the lamps, the toaster, the hair-dryer, everything is hardwired. There are no plugs in the house. Everything is wired directly into the electrical system, so you can’t easily replace anything.
The standard interfaces, electrical sockets, between the electrical devices and the electrical system are not there. You couldn’t replace your toaster without calling an electrician. One might say that the lack of interfaces has made things very difficult to deal with, and thus you decide not to buy the house.
Do you see a pattern here?
Okay, enough stories. I’ll say it. In your code, the interaction between objects should be as thin and clean as possible. You should ask for precisely what you need and nothing more. If you need something, you should ask for it — have it “delivered” to you instead of trying to create it yourself. That way, just like with the cake and the cashier and the house, things stay neat and clean and flexible. I think code that is neat and clean and flexible sounds pretty good.
That is the essence of dependency injection. It’s nothing more than that. Dependency injection is a twenty-five dollar term for a ten-cent concept: If you need something, ask for it. Don’t create things yourself — push that off to someone else. Every class of any substance will likely need help from other classes. If your class needs help, ask for it; don’t try to “do it yourself.” Remember, every time you try to “do it yourself,” you create a hard-coded dependency. And again, those should be avoided like global variables.
It is, as we shall see, really that simple.
One thing to notice is that I haven’t yet used the word container. In fact, here’s a tweet of mine:
That may sound a bit weird, but it makes an important point: Doing dependency injection and using a dependency injection container are two very different things. They are related — the latter making the former easier to scale — but they are not the same thing. I’m not even going to talk about a DI container in this article at all.
So What Exactly is Dependency Injection?
By now, you probably are wondering what DI exactly is. Well, according to Mark Seemann, author of the wonderful book “Dependency Injection in .Net,” Dependency Injection is “a set of software design principles and patterns that enable us to develop loosely coupled code.” That’s a good definition but a bit antiseptic. Let’s explore a bit more deeply.
If you are going to inject a dependency, you need to know what a dependency is. A dependency is anything that a given class needs to do its job, such as fields, other classes, etc. If
ClassB to be present to compile, then it
ClassA is dependent on
ClassB. Or in other words,
ClassB is a dependency of
ClassA. Dependencies get created when, well, you create one. Here's an example:
In the above code, we have created a hard-coded dependency to
ClassA. It's just like the hairdryer or the toaster discussed earlier, it is hard-wired into the class.
ClassA is completely dependent on
ClassB is coupled to
ClassA. Tightly coupled. About as tightly coupled as you can get. And tight coupling is bad.
A word about coupling
Coupling is the notion of one thing being dependent on another. Tight coupling is when things really depend on each other and are strictly tied. You can’t compile Class A without the complete definition of Class B being present. B is permanently stuck to A. Tight coupling is bad — it creates inflexible code. Think how hard it would be to move around if you were handcuffed to another person. That’s how a class feels when you create hard-coded dependencies.
What you want is to keep the coupling between your classes and modules as “loose” as possible. That is, you want your dependencies to be as thin as you can make them. If you have a class that needs a customer name, pass in just the customer name. Don’t pass in the entire customer. And as we shall see, if your class needs another class, pass in an abstraction — an interface, usually of that class. An interface is like a wisp of smoke — there is something there, but you really can’t grab on to it.
What to Do?
Well, the first thing to do is not to create
ClassA. Instead, inject the dependency via the constructor:
So at this point, we have two types of coupling: one where the first class creates an instance of the second class and one where the first class needs the second class (i.e., where it is injected). The latter is preferable to the former in that it involves less (looser) coupling.
And that, friends, is the essence of DI. Inject your dependencies instead of creating them. That’s it. That’s dependency injection. If you understand the notions that you should code against abstractions and that you should ask for the functionality that you need, you are well on your way to understanding dependency injection and to writing better code.
Dependency injection is a means to an end, and that end is loosely coupled code.