4 ms·
Let's take as an example dependency injection. The genesis for DI was to facilitate the testing of objects that are dependent on objects that are not easily con
by revscat 13y ago
Let's take as an example dependency injection. The genesis for DI was to facilitate the testing of objects that are dependent on objects that are not easily constructed, such as DAOs. And the whole reason this was done was so that these classes, the ones that have a DAO dependency, could be more easily and quickly tested.
So, in order to use (constructor) DI you would have a constructor that looks like this:
public InvoiceController(InvoiceDao invoiceDao) {
this.invoiceDao = invoiceDao;
}
The key point to remember here is that this has been done so that in our unit tests we can inject a fake InvoiceDao. Now, let's say that our InvoiceDao class has one and only one constructor:
public InvoiceDao() {
// sets up connection parameters, etc.
}
Then the constructor for InvoiceController could be simplified to:
public InvoiceController() {
this.invoiceDao = new InvoiceDao();
}
This is quite a bit cleaner from an API perspective, and that is the entire point. This is only a simple example. For more complex classes, with multiple dependencies, it really becomes cumbersome. What if InvoiceController also needed access ReporterDao? Well, then you need to add that as a parameter to the constructor as well. Your API is made more complicated, all in an effort to make testing possible.
This does not, of course, invalidate the benefits of unit testing, which are many. But it does expose a negative that is not frequently acknowledged, and that's what DHH is talking about, and what Kent has failed to address.
- kasey_junk 13y ago"The genesis for DI was to facilitate the testing of objects that are dependent on objects that are not easily constructed, such as DAOs. And the whole reason this was done was so that these classes, the ones that have a DAO dependency, could be more easily and quickly tested." I disagree with this central premise. If you have introduced DI simply for testing reasons than you have missed the point. The reason for DI is that a very common point of change with software is at the interaction between clients and services. Tight coupling at this point of change is usually a design smell. That DI enables automated testing is a by product of it being loosely coupled not the point of the loose coupling. That TDD drives you to this sort of solution, all other things being equal, is one of the advantages of TDD. If DI has become cumbersome and is making your code hard to reason about/read/maintain etc then blame DI, not the testing. You also might want to consider the higher level architecture of your solution if you are finding yourself with lots of complicated dependency chains.
- revscat 13y agoThat wasn't the central premise. The central premise is that there is a trade-off in class design when doing unit testing.
- kasey_junk 13y agoAnd I say that if you are making design decisions BECAUSE Of testing you are doing it wrong. The premise of TDD (and one I've found true) is that there is a natural correlation between easily tested code and well factored code, not that you should be compromising your design for testing. Thus the original comment about bad api design as a result of TDD rings false with me. You have apparently encountered cases where people have introduced unnecessary DI in the name of ease of testing when in reality they should have been fixing their central design problems. Software with long dependency chains and complex IoC containers are not easy to test, though looking through the comments it seems that many people have fooled themselves into thinking they are.
- jshen 13y ago"The reason for DI is that a very common point of change with software is at the interaction between clients and services. Tight coupling at this point of change is usually a design smell." The problem with TDD is that if it is NOT a common point of change, TDD adherents often say that you should introduce DI anyway to make testing easier. "One day you might want to change out the whole database, so you should abstract out the DB into a blah blah blah pattern, with a layer of the rah rah rah pattern between to ..."
- dragonwriter 13y ago> The problem with TDD is that if it is NOT a common point of change, TDD adherents often say that you should introduce DI anyway to make testing easier. To the extent that's a problem, its not with TDD, nor with DI, but with the particular people giving the advice. Why do people think that "Some of the people who follow X give bad advice Y" is a criticism of X when Y is not something that that X requires?
- DatBear 13y agoBut that's not true at all. The genesis for DI was nothing close to that (as I learned it). DI's main purpose isn't testing, it's flexibility. DI isn't there solely for testing, or if it is, you're probably missing the point of it.
- icedchai 13y agoRight. However, in many DI-heavy apps I've seen, nobody ever makes use of that flexibility. Otherwise it's just extra, unnecessary complexity, sitting around in verbose XML files and annotations, causing code clutter. These are "enterprise" Java apps with Spring, etc.
- revscat 13y agoThis has been my experience as well. The core of Spring -- IoC/DI -- solves a problem that is largely of its own making. I've had better luck and cleaner code without it than with it.
- DatBear 13y agoI personally don't see how a couple parameters in the constructor are adding so much complexity that you'd be better off without DI. A good DI container can make it much easier to follow a pretty good amount of general best practices. Single Responsibility, DRY, and Open-Closed Principle to name a few.
- deleted 13y ago[deleted]
- dragonwriter 13y ago> Let's take as an example dependency injection. The genesis for DI was to facilitate the testing of objects that are dependent on objects that are not easily constructed, such as DAOs. Really? Because I've always seen DI advocated for loose coupling, with testability just coming along for the ride the way it does in general with loose coupling, I've rarely seen it advocated for testability independently of the broader architectural motivation for loose coupling.
- nvarsj 13y agoYou miss the point of DI. DI is solving a problem common to non-trivial OO apps - where do you create the objects? Every sane approach to this tends to lead to a form of DI. Spring is a bad example of DI because it encourages an implicit, DI-everything style. Take a look at Guice or other frameworks where bindings are explicit.