This stream of conscious post started from a rant posted by by Jeremy D. Miller on the CodeBetter.com blog. I’m going to emphasize something I just said: this is a stream of conscious post. It’s going to ramble a bit.
My very first thought was that Jeremy was rambling. No offense to Jeremy, but it was hard to decide what side of the fence he came down on solely by that post. Which means his mini-rant was about as coherent as this post is going to be ;).
OK, so Jeremy is a DI proponent (obviously, as the author of StructureMap, one of the more well known IoC Containers for .NET languages). He appears to have taken exception to a post by "Roy". Specifically, he didn’t like references to being "pragmatic" by not using DI. Given some of the quotes he started the post with, I can understand why his hackles were up. When you first read them, they sound awfully… uhmm… offensive isn’t the right word, but that’s the only one coming to mind. They seem to be confrontational in nature, any way.
However, since I had problems fully understanding what Jeremy was ranting about (BTW, he calls it a rant, so I’m not criticizing him for that), I read this small post multiple times. Second and third readings only showed one of the quotes to really be out of line: "you’re just a zealot". That one is obviously a sentence fragment, however, and without context you can’t really judge it.
At this point, I had to think a bit. You see, I’m one of those "pragmatic" types that’s not fond of the overuse of any design pattern, and who has particular problems with DI. So, I started to see Jeremy’s rant on this subject to be basically the very thing he’s ranting on. A dismissal of ideas with no reasoning or subjective analysis of differing view points.
However, that little post wasn’t enough to judge anything on… especially when it was so difficult to understand what point Jeremy was trying to make. So, I clicked through to the post by "Roy". Turns out to be a post by Roy Osherove. He’s a lead developer of TypeMock, a .NET library used to auto-generate Mock objects when unit testing. I initially heard about TypeMock from an article on CodeProject. There were things in that article I could pick-nits with, but overall, I agreed with the premise of not designing for testability. When you do that, you make the code more difficult to consume in the normal case. A Mock object that allowed you to accomplish testing with out having to design your code for testability resonated with me. I never got around to using TypeMock for anything (mostly because my day job isn’t in .NET, though that’s changing :), and I didn’t have time outside of work because I’m a daddy), but I still recommend the article to people as at least "food for thought".
I read Roy’s post, and I couldn’t find any of the quotes he made. I started to become a little paranoid. See, just a couple of days prior I’d had a discussion with Robert Eisenberg about his Caliburn MVC/MVP framework where I’d made some quotes at least somewhat similar in nature. I’d talked about not liking IoC Containers for "pragmatic" reasons, though going back and reading the e-mail archive now, it looks like I didn’t use that word. Isn’t paranoia fun? 😉
Any way, I’m going to take the opportunity here to elaborate on why I think DI and IoC Containers in particular are used too much.
Design patterns are a good thing. They give us a language for discussing architecture, and provide us with solutions to difficult software engineering problems. The GoF book was probably one of the most important books ever published in our field. Unfortunately, however, the real world differs from the theoretical one far too often in our industry, probably because we’re still in our infancy.
For several years my day job required working with Java, specifically creating web applications. My opinions on design patterns were influenced heavily by this. You see, for some reason this world took to design patterns like a drug addiction. I’m going to guess that this had something to do with both Java and GoF being the "new kids on the block" at about the same time, so people fresh out of school or self teaching themselves considered both to be the silver bullet that would save the world. Well, it’s been said before, there is no such thing as a silver bullet.
I’m no fan of a Java. Never have been, and never will be. Patterns, however, I do like. They are incredibly important. I could have easily become one of the crowd that treated them like a drug addiction. Except… I had to be a Java web developer for a few years. Boy, was that an eye opener.
Here’s just one simple example of what I faced on a daily basis. Our application required a new page to do something extremely trivial. Let’s say all it did was prompt the user for an e-mail address to send them some information. I misplaced my book on Struts, and it’s been a very long time since I did this, so I may remember some things incorrectly, but this is the impression that was left. To add this simple little screen, one had to modify 3 source files and 2 configuration files! I fully understand the concept of decoupling your objects to avoid dependencies (just look at my journey into M-V-VM within WPF), but anyone that thinks this sort of architectural design is good certainly has a destructive addiction, IMHO. That is not maintainable. You can’t take a competent developer who knows the Java language but not the particular framework and have any expectation that they’d be able to fix a bug in that soup. He wouldn’t know where to look! The worst part is the configuration, since problems there are easily found only after acquiring a LOT of experience with the behavior of the framework.
Design pattern’s were abused here. In the pursuit of purity in design the framework failed to account for their customer, the application developer. For him, there’s a time and a place for some of the power that these patterns provided, but in the majority of the use cases he is instead required to write unmaintainable spaghetti code, "because some day he may need to do X".
After that, I was sensitive to the use of patterns, and started to notice they were often overused even in every day code. I wasn’t the only one noticing this, either. Everyone seemed to be using the Singleton pattern, despite the fact that a Singleton is nothing more than a global variable in fancy clothes, and most designers understand the dangers of global variables. Next up on the stage was the Factory. I can’t tell you how many times I’ve run across the Factory pattern, where the factory only instantiated a very small handful of instance types and all the calling sites asked the factory to create specific types through the factory. This means the developer missed the point about why a Factory is useful, and when you should use one, but hey, his code is well designed because he used patterns! A classic phenomenon was taking place. Developers had a new hammer, and now every problem was looking like a nail.
So, when do I think you should use patterns? First, they should only be (at least blatantly) used in implementations. If you’re writing framework code and I have to fully understand the Memento pattern to write code using it, the framework’s public interface is a failure. Obviously patterns may indeed be exposed in places, but if the developer has to think too much about the pattern or adapt his code to fit the pattern, then you’ve got a code smell. For instance, .NET events are a form of SubjectObserver that are exposed to developers, but they don’t require deep understanding of the pattern or large changes to client code.
Next, they should only be used when absolutely necessary to solve a problem. That should be obvious, but as I’ve been trying to point out, it doesn’t seem to be so obvious to a lot of developers.
This is where I’m going to tie all of this together into a neat little package and bring us back to the initial topic. IoC Containers, I believe, are an overused design pattern. Every single discussion about IoC starts out with discussing unit testing. DI allows you to replace a dependent service with a Mock object in order to test only the the code that consumes the service. Oh, how appealing that sounds! Because we want to test everything, and IoC/DI allows you to replace services dependencies in order to test, we should be using it everywhere!
Unfortunately, academia hasn’t spent enough time in the real world. The first problem lies in DI itself. There’s a tendency to pull out too much code into services. If there’s no need for the decoupling you may be making things harder to maintain by making the separation! This is tricky, of course, because you can easily go too far in the other direction, making "god objects", if you don’t consider decoupling. I simply don’t think there’s a universal law that can be applied here. You have to look at things in a case by case basis to determine which design is more appropriate. However, the "DI solves unit testing" crowd have a predisposition to one design, and therefore often make the wrong choice, making their code less maintainable.
The worse problem, however, isn’t with DI but with the user of an IoC container. You see, it’s at this point that you push the design pattern out of the implementation and into the interface, thus making the client code much less maintainable. If, for the normal case, I have to understand the concept of an IoC Container just to consume your code, you have a massive code smell. It becomes exponentially worse when I have to deal with a container solely because there’s only ever going to be two concrete services: the real one and a Mock used for testing. The Mock service isn’t real. It serves only to make unit testing easier. That’s an implementation detail that you’ve just leaked into your public interface, folks!
TypeMock claims to be an alternative that "corrects" this issue. It is, however, not the only alternative. Without TypeMock we could still make use of DI without leaking our implementation so badly. If you don’t have the need for swapping out services for anything other than testing, don’t use an IoC Container! Your normal consumers should not have to know that you’re using DI at all, which becomes unlikely when using an IoC Container. In the vast majority of IoC Containers, it’s the consumer that has to configure the container, which is a very messy implementation leakage. It is possible to design your objects so that the container configuration has to be done only in the degenerate case, but you hardly ever see that done :(. However, even then, you’ve got problems with maintainability because you’ve used a container. When I have to maintain the object’s code, I also have to maintain the container’s configuration. This is complexity that exists only because we have one degenerate case: unit testing. Forget the container, man. Hard code the construction to the actual service, and then let the unit test replace this with a hard coded Mock. Consider doing this in some manner that doesn’t leak the fact that DI is used at all (which is basically what TypeMock does, BTW). In other words, don’t expose the injection mechanism in the public API. Use another solution, such as reflection or even conditional builds that expose the API only for unit testing.
Academics are screaming at me right now. "You don’t know that there’s only two concrete services that will be needed here! Some day you might need another implementation!" There’s some truth in there, but the pragmatic programmer will know to ignore this. If/when it’s determined there’s other concrete service types required, because we’ve accounted for this already by using DI in the implementation, it’s a trivial refactoring to expose this to the consumer, even going so far as to using an IoC Container.
You see, I don’t think IoC Containers are not useful. I just think they are incredibly over used. Worse, their over use seems to be caused solely by an extremely degenerate use case: unit testing.
However, to give Jeremy credit, he has a very good point in another post on this subject.
So, to wrap up: TypeMock does not end the idea of designing
forwith testability, and is not going to let you get away with a bad design anyway.
Even ignoring TypeMock and using the other solutions I talked about, you may well still have to pay attention to making the code testable. Just as an example, if database access is hard coded all over within methods in an object, you’ll find testing the object to be extremely difficult to do, even with TypeMock. The good news is, refactoring this code to make it testable is highly likely to also make it more maintainable, provided the refactorings are careful to not leak implementation details into the interface and don’t use a minimalist approach that doesn’t over use patterns ;).