Thursday, December 20, 2007

Dependency Injection

Dependency Injection is an "object oriented design pattern" for creating loosely coupled objects. It has nothing to do with needles, and if it makes you think of the Breakfast Club that's not my fault.

Lets say you're writing a class whose job is to find a certain configuration file. Its going to look in a predetermined expected location, and if it doesn't find it there it will ask the user to select it. This object is dependent on the user. And in code, it is dependent on some kind of UI for asking the user for the file.

If we're in C# you can write this object so it creates an OpenFileDialog (which doesn't have the same Memory Leak causing behavior as a regular form, by the way) and displays it to the user. However, this would not be a loosely coupled design. What if you wanted to change this to use the Vista open file dialog? Or what if you wanted to make it force the user to select a valid file by continually asking them until they select one? You'd have to edit the main class. And you'd have to be careful that the user prompting logic didn't become intertwined with the rest of the class' logic.

What can we do? Let's inject our class. Let's inject it with dependencies. Or more accurately, classes that take care of its dependencies for it.

Create an interface like IPromptUserForConfigFile which contains a method that returns the path the user selected as a string. Now modify the constructor of your main class to accept an instance of an IPromptUserForConfigFile class. The main class simply calls the method on this interface, all the details of how the user is prompted are abstracted away. Plus you can change how the user is prompted at any time by simply passing in a different class that implements IPromptUserForConfigFile.

Dependency injection seems pretty simple. It gives you loosely coupled objects, which are very orthogonal, and possibly best of all it makes it easy to unit test with the help of a library like nmock.

What are the downsides? You now have an interface and a class that you otherwise probably wouldn't have created (since you haven't actually had any practical DEMAND for loose coupling yet). And you also have to construct a class that implements the interface and pass it into the main object's constructor. There are libraries to help with that part, like Spring.NET. I've never personally used them, but they exist. Actually, when I've used this pattern I've built in a default implementation of the interface to the main class and instead of passing in the interface object through the constructor I allow you to override my default with a property. This clearly violates some of the loose coupling, but to be honest I'm really not using this pattern for the loose coupling, I'm using it so I can Unit Test.

The biggest downside here would seem to be that the size of your code base has increased. And this has happened to enable loose coupling, but mostly to allow for Unit Testing. Is this a good thing?

When I first started learning about Unit Testing my biggest worry, apart from how much more code I'd be writing and maintaining, was that I would start writing strangely designed code just so I could Unit Test it and not because it was the right design for the situation. You can imagine all kinds of terrible things. Classes with all public methods. Classes with only a single static method that is only used by one other class. Classes with tentacles and suction cups and slimy oddly named and mostly irrelevant properties.

However, I was surprised at how often my design changed for the better due to thinking about testing. My first few unit testing attempts didn't result in any tentacles or other horrible things. That is, until I encountered dependency injection. "Look! I'm making my code loosely coupled!" I thought to myself, "That's a good thing! This Unit Testing really works!"

But dependency injection is a very slippery slope. After all, the majority of classes you write are riddled with dependencies. Things like databases, framework code, web services, user interfaces, other code libraries you've written, etc. Should you seriously create a class to wrap each one of these and then pass those classes into other classes that apply the "business logic" and "glue" between how those dependencies are used? Imagine how huge your code base would be then! And you certainly can't create static classes if you're planning on passing them into another class through a constructor. And of course now that you've created all these objects that wrap various dependencies, you're going to want to reuse them. In .NET that means putting them in their own assemblies. Are we going to end up with 500 assemblies with complicated dependencies? Where do we draw the line?

An object mocking framework like TypeMock (not free!) would help alleviate the need for all the "injection" going on here just for the sake of testing, though you'd still need to create many objects. I need to look into that product more to decide if its worth the cost (its quite expensive). If it is, then the line can be drawn with a question, "Do I need this to be loosely coupled?" If it isn't, then the line needs to be drawn with a different question, "Do I need to mock this for testing or to have it be loosely coupled?" The first question is pretty easy to answer. The second one... maybe not so much.

Lets end with a string of questions. Do you unit test? Do you mock objects? What mocking library do you use? How does dependency injection make you feel?

1 comment:

  1. Ill start by answering the questions at the bottom of your post.

    1) Yes we unit test, but poorly.
    2) We do mock but we are trying to move away from it.
    3) We use NMock and RhinoMock (old projects on NMock, newer on Rhino)
    4) Warm and fuzzy with a bit of uneasyness.

    What you described as problems for DE are exactly what we see. In order to prevent having dependencies we sometimes go overboard and create a bunch of interfaces and factories and all this other crap. The factories really are for testing because you can inject whatever mock object you want the factory to return as long as it matches the interface.

    I hate that. It seems wrong in some way and almost seems hackish.

    In testing there are 2.5 schools of thought. Those who support mocking and mock every external dependency. Those who prefer end to end testing where you write specific unit tests, but then as you go further up the dependency tree you dont mock those further down. And those who are in group 2 but find a way to mock out data connections.

    I like the third option, but ive never really done it. If you look into how rails does unit testing it kind of follows this approach. It does this by allowing the test to inject sample data into the data stream from a data connection. So i can write yaml that lets you put in records that match what you want.

    We are redoing our architecture so any dependencies are called in what will really be our model, and then calls to those methods will be in our controller. We wont test the model because the code is simple. Its call X service returning Y. Something that we do all the time and the code is identical. We will focus on testing real business logic in the controller.

    ReplyDelete