Dependency InjectionMore than a testing seam
Jacob Proffitt has a post about Dependency Injection, which he doesn't seem to like very much.
The real reason that DI has become so popular lately, however, has nothing to do with orthogonality, encapsulation, or other "purely" architectural concerns. The real reason that so many developers are using DI is to facilitate Unit Testing using mock objects. Talk around it all you want to, but this is the factor that actually convinces bright developers to prefer DI over simpler implementations.
I do wish that people would admit that DI doesn’t have compelling applicability outside of Unit Testing, however. I’m reading articles and discussions lately that seem to take the superiority of DI for granted
While the ability to easily mock the code is a nice property, it is almost a side benefit to the main benefits, which is getting low coupling between our objects. I get to modify my data access code without having to touch the salary calculation engine, that is the major benefit I get.
I have had more than one occasion to experience the result of not doing so, and I have seen the benefits of breaking the application to highly independent components that collaborate together to create a working whole.
Take that and join it with a powerful container (such as Windsor), and suddenly you gain a whole new insight into the way you are constructing your application, because the moment you are tunneling a lot of the dependencies through a single source, you find that there are a lot of smart things that you can do. Decorators, interceptors and proxies come immediately to mind, but there are quite a few other options as well.
All of this becomes particularly exasperating in the dead silence regarding TypeMock.
Type Mock is a great library, and you can check this blog to see my discussion with Eli Lupian about it. The main weakness of Type Mock is its power, it allow me to take shortcuts that I don't want to take, I want to get a system with low coupling and high cohesion.
And from the comments to that post, again by Jacob:
How can you say that dependency injection (I'm not taking on the whole inversion of control pattern, but I might. Jury's still out on that one.) creates loosely coupled units that can be reused easily when the whole point of DI is to require the caller to provide the callee's needs?
Because there isn't a coupling from the unit to its dependencies, and there shouldn't be a coupling between the caller to the callee's dependencies. But even without the use of an IoC container, you can get a lot of benefit from DI, just the ability to pass a CachingDbConnection instead of an IDbConnection is very valuable, and not, this is not something that you can just put in some sort of a factory or provider, because you want that kind of decision to be made on case by case basis.
Ugh. I wish Ayende would admit that the only advantage to DI in his examples is to allow the mock objects to work, is all. There is no other compelling reason.
Sorry, but I don't think that this is the case at all. Dependency Injection isn't (just) for testing, it is to Enable Change.
More posts in "Dependency Injection" series:
- (23 Aug 2007) Separating the Container
- (21 Aug 2007) Applicability, Benefits and Mocking
- (18 Aug 2007) IAmDonQuixote ?
- (18 Aug 2007) More than a testing seam
Comments
"I wish Ayende would admit that the only advantage to DI in his examples is to allow the mock objects to work, is all. There is no other compelling reason."
I think Ayende has a number of great examples of DI being used for more than just mock objects. See his article here for a very comprehensive explanation of what's possible:
http://msdn2.microsoft.com/en-us/library/aa973811.aspx
Personally I think that DI is a great thing and I like to use it.
But the main problem with the current (w/o TypeMock) TDD/DI approach is, for me, interface-driven architecture. I like DRY and I do not like to repeat my method definitions over and over again.
Also, interfaces make APIs less understandable. While I can get object interactions by looking at interfaces, more common problem with new APIs is "this method wants type X, what should I pass?". When the type is IX, it gets harder since, for example, I have no constructor.
As for me, interface that has a single implementor is a useless waste of code lines. I like that this is rarely the case with .Net Framework. While I see no other way to work around technical limitations of mocking concrete classes (except TypeMock), I still view it as a bloated architecture and a (required) hack.
DI can be used in a lot of different scenario's, completely unrelated to TDD. For example: pluggable validation objects, authorization objects etc. in entities. You can inject them with DI for example, so you can write the app once and for example via configuration inject the right authorization code or validation code written in different projects than the main system. This is abit like AOP but you don't have to use a weaver, just inject the code at runtime and you're set. :)
Furthermore, DI shouldn't be overused, as in: property setting with value types via DI, I'm not sure if that's really useful. It can be a handy tool which could make writing the application more easier because you don't have to hard-code certain code inside another class for example.
While I can see the benefits of DI and the architecture that it enforces, I have yet to "feel the love" from a system architected this way due to the learning curve and associated costs (doing business with a blunt saw anyone?).
I think this may be the reason a lot of people do not see the benefit of some of these approaches to building an application, without being able to grok the whole picture in your head, the only real way to see this is better is to live through the pain of changing a DI-less system and then live through the joy of changing a DI based system.
Sometimes knowledge (in the form of lessons learned from your mistakes) >> wisdom (lessons learned from other peoples mistakes).
I started using Windsor before I even heard of TDD actually, even when I don't use an container I still code with DI in mind, it makes the code easier to work with.
@Andrey,
DRY does not disallow using interfaces, if it did then all of C++ would be in violation of DRY. It even allows duplicates of the code, as long as only one of them is the "authoritative representation", although thats generally better to avoid if possible. And neither in VB nor C# will you have to retype the implementation, the IDE can expand it for you. Although interfaces aren't the only way of doing it, abstract base classes or virtual methods works quite well too for enabling testing (case dependent, of course).
I tend to disagree with using interfaces all over the place, just for the sake of easier mocking & testing. If the business application really needs an extensibility point at a certain place, then interface is the way to go. But to put all over interfaces without a real need and only because of unit testing seems like allowing unit testing to "distort" your domain model...
This is, of course, only philosophically speaking, technically speaking it's questionable what is the smaller evil - adding interfaces all over the place or making all methods virtual (I hate that virtual is not default in .NET) which is also a kind of manipulation related to easier unit testing ... Interfaces should always be used, if the domain model really needs them, that's for sure.
<sarcasm>
DI/IoC is only for TDD/Mocking and AOP is only for logging and tracing and BTW, the world is flat...
</sarcasm>
IoC is perfectly for decoupling parts of your applications and allowing a local "service"-structure. Interfaces are part of the game in .NET and Java, but the pattern also applies to Python or Ruby, which should be your languages of choice if you dislike interfaces.
An example that has to nothing with testing, is changing implementations on run-by-run-basis. I.e., if network's available, use a component to lookup contact data from the companies LDAP, if not, use locally cached data. Neither part of the "real" application need to know how the data is fetched, only the container setup.
You might call that IServiceProvider, I call it IoC.
-Markus
When writing an application, you may well be able to analyze in advance to see where you need pluggability points and provide interfaces there. Adding interfaces where you don't anticipate any use for them would of course be a violation of YAGNI.
But when writing frameworks, YAGNI does not apply to the same extent. There is much less opportunity for knowing in advance where users may need pluggabilty points.
So when writing frameworks, you should provide interfaces for pretty much every class. The reason for this is so that your framework code binds to the interfaces rather than to default implementations, allowing users to substitute implementations of any parts of the framework.
This means that in a framework, you'll have lots of cases where there's only one implementation of an interface. The only thing that means is that the framework only provides one default implementation of that interface - just because the framework doesn't supply the alternative implementations doesn't mean that it can go without the interface, following some rule of thumb that "an interface with just one implementation (from the same provider!) is a waste of time".
The reasons why you want your framework code to bind to interfaces rather than abstract base classes are that:
1) .NET only allows single implementation inheritance - you don't want to have to waste your base class on type compatibility!
2) The base class may not be pluggable enough, for example sealing something that the user would need to extend or replace.
So, adding interfaces all over the place may be a bad idea for the application programmer, but an excellent idea for the framework developer (such as the .NET framework developers).
/Mats
I agree completely with Mats comments but I would go further by saying that even within a single application different parts of the system can use different approaches.
For example if my business code calls out to my data access (repositories), which I tend to avoid, then I want it to be to an abstraction.
However when I'm using the business classes from a higher layer I'm not so worried about accessing them through interfaces unless those interfaces are designed specifically for that sort of usage (ISP).
In either case I'm choosing the approach that I think is most suitable, not blindly going down the DI or TypeMock approach simply because on is "better".
@Deyan Petrov - Agree completely, I've tried the interface based approach in those situations to allow me to get in with Rhino Mocks and it was a mess. If they are needed then great.
Comment preview