Creative Commons License
This work is licensed under a Creative Commons Attribution - Noncommercial - No Derivative Works 3.0 United States License.



















Technorati blog authority

My thoughts on best practices in software architecture and development as a whole (with an emphasis on Java/J2EE).

Tuesday, June 05, 2007

I finally get it - Dependency Injection

So for the past few years I've been reading about Spring, Inversion of Control and Dependency Injection and although I understood the inherent advantage of dynamically wiring things together I never really had an application that required or even suggested a need for that level of dynamism (is that really a word?).

Frankly, I'm embarrassed to say I didn't "get it" because I really didn't see the application of this pattern/idiom.

Then a few days ago I finally got around to listening to the first Google Developer Podcast and in it the hosts interviewed Bob Lee of Guice fame.

Finally his explanation of Dependency Injection (DI) clicked for me as to WHY I would use it.

"Dependency injection . . the reason for doing this is because it makes your code more testable, and results in a little less boilerplate code"

Finally *that* made sense. I guess a few things have come together to help me - I've been reading a lot about Mock objects and started using them a bit in my unit tests, plus I have been trying to figure out how best to express pretty generic business flows with objects whose complexity can vary considerably (different customers might need varying amounts of data associated with each business action).

For example, a simple thing as a book purchase could have an object with hundreds of attached data fields depending on many requirements - new or used book, reporting etc. There are also many actions available. The best way to handle such flows, I'm thinking, is to have an Interface (IOrder.java) and then pass it in to some piece of code that is aware of the flow.

Here's a simple possible example . . . .

public void execute(IOrder myOrder) throws OrderFlowException

{

if(myOrder.validate()) {

myOrder.execute();

}
}


Then the end customer can create their own implementation / override a default, with their own fields, just as long as it satisfies the interface.

I guess too many of the previous articles I read were a bit too much "Silver Bullet" oriented - which makes me gag! But often this bit about reducing dependencies just didn't sit right - logically if there's a dependency then there's a dependency! The core dependency is still there so that's not a silver bullet!

Yes Dependency Injection helps reduce dependencies between implementations but that's less than one-third the battle to getting to loose coupling IMNSHO. On the downside, extra layers of indirection just make your code hard to understand and the dependency still exists (at runtime).
Anyway, kudos to Bob Lee as his description was something I could "buy" - DI is something that makes my code more easily unit testable and which reduces boilerplate code. Makes another nice tool for my toolbox (not a whole new toolbox by itself!).

Perhaps I'm still a little lost (I still don't get the big deal) but not so totally anymore.

Anyone else as lost on this topic as me? Any resources I should read to help get up to speed? Or perhaps I should just dive into Spring / Guice and get it over with :-)

Labels: , , , ,

12 Comments:

Blogger Bob said...

Check out this talk: http://crazybob.org/2007/06/beijing-guice-talk-on-youtube.html

It illustrates the concepts from the podcast with code.

6/05/2007 2:07 PM

 
Blogger M Easter said...

Great, candid post... It took me a long time to "get it" and it was a revelation when I did.

My personal revelation was re-discovering the profound beauty of interfaces and the notion of "the ether", which I'll expand on on my site.

Note that in terms of DI, Spring certainly has competition with Guice. Part of the buzz about Spring though is that it offers more than just DI.

6/05/2007 10:21 PM

 
Anonymous Peter Bell said...

In addition to making code more testable, DI also helps resolve dependency chains.

With big apps you often have beans that depend on other beans that in turn depend on other beans (and so on). Some of them need fully initialized beans that they can call methods on in their constructors, others just need the beans before you actually call them.

Trying to manage those dependencies manually is a royal pain for a really large app. With your DI engine of choice, it takes care of all the necessary recursive dependency lookups and initialization. It isn't rocket science (I wrote my own DI engine in 400 lines of code), but it is nice to have an engine that takes care of such things for you.

Of course, this isn't an excuse for making your beans too interdependent, but there are real use cases that require these levels of coupling.

6/06/2007 10:32 AM

 
Anonymous Anonymous said...

Just dive into it. DI is one thing, where you make an effort to have a class do one thing and do it well without worrying about other stuff; but Spring takes it a step further with lifecycle management.

Imagine OO without having to worry about how and when objects created and placed into the context they are used. A lot less crap to deal with it, just "code that actually does stuff".

6/06/2007 1:55 PM

 
Blogger Laird said...

I think of it as, look, you have two worlds. There is the world of the problem domain, and the world where the problem domain is assembled and disassembled. Dependency injection/IoC offloads the responsibility for managing the second world from you onto some hopefully third-party code. Your code (and your tests) becomes responsible for focusing intently on the first world.

The dirty secret--well, maybe not so secret--is that most of the enterprisey stuff we're all usually stuck doing consists mainly of tasks in the second world. That is, most of the time that I spend, anyhow, is wrestling with some legacy system to get it to produce objects that are capable of modeling the first world properly. So if you're not careful, your DI configuration will "outweigh" your actual code by pounds and pounds. If your DI engine is easy to work with, that's not a problem; if it's XML, I'd rather program in Java.

6/06/2007 2:46 PM

 
Blogger mike said...

I watched that podcast also. I thought that they did a really good job at explaining DI. However, when they started getting into the implementation of Guice, I think I threw up a little in my mouth. I would much rather configure xml than write "Modules" in java.

BTW: Spring rocks.

6/06/2007 6:10 PM

 
Anonymous Anonymous said...

I still don't get. If you have an object to test then create the object to test and pass it the objects you want to test with. How hard is it that you need another magic assembly layer?

If you use TDD then DI does nothing for testability at all.

As your example shows the key is to use interfaces. You can then create implementations and compose them in your code where people can actually see what you are doing rather than hiding everything in some configuration file.

6/06/2007 8:31 PM

 
Anonymous Anonymous said...

what really helped me to get it, is the hollywood principle:

"don't call us, we'll call you"

6/07/2007 3:10 AM

 
Blogger Mike G. said...

"Large Scale C++ Software Design", by John Lakos.

All the objections that Lakos raises to tangled dependencies in C++ code apply equally to Java, and he advocates exactly that abstraction technique in order to allow testability.

It's very important to be able to test your code _only_ in terms of other things that are already tested, and having a low level interface that can have a test implementation makes many levels much more testable.

6/07/2007 7:37 AM

 
Anonymous Anonymous said...

You can also check out the article by Martin Fowler where he explains this concept really well here-> http://www.martinfowler.com/articles/injection.html

6/07/2007 8:42 AM

 
Blogger wtanksley said...

"If you have an object to test then create the object to test and pass it the objects you want to test with."

Correct -- in other words, your test code is performing dependency injection on your (normal) code. That's exactly the point.

It's not really about a fancy framework like Spring or Guice. It's about your code's architecture.

The principle is: don't make your business objects responsible for fulfilling their own dependencies. Don't mix business logic with configuration logic. As far as your objects are concerned, everything they need should be "just there" when they need it.

6/07/2007 10:30 AM

 
Blogger Douglas said...

Like many people it took me several months to really "get it." Until that point, honestly, I thought the Spring hype was overblown. But now I cannot overstate how happy I am with DI (not so much the XML config files though).

Most of the comments above have missed the main benefit of using DI: decoupling your classes from the *concrete* implementation. The dependency is reduced from dependency on a particular implementation (JDBC, RMI, SOAP, mock object, whatever), to dependency on a generic interface. Therefore you can give the exact same bean a mock object during testing (and without even involving Spring at all), and the real implementation that hits the database, network, etc when deployed. Otherwise, if your bean itself instantiates the production implementation, how can you exercise that bean in isolation, without databases, networks, etc?

For this to work, the interface is almost always an in-house class because it is at the domain level. Most standard interfaces are inappropriate because their domain is that of software algorithms (Map, List), databases (java.sql.Connection), etc. Instead, your implementations of your interface *use* the standard interfaces to build data structures or connect to the database, but they do that to meet the end of your domain interface.

One classic example of this is how to get external data (such as a Customer). In production it's typically coming from a database, but in a unit test it's coming from a file or just inline data. So the interface (I prefer a name like CustomerSource) would have a method getCustomerById(id) and/or getAllCustomers(). Because it is so tailored to your domain it can be very generic and yet very specific *at the same time* (in different aspects). That makes it very easy to write 5 different implementations for.

6/07/2007 12:35 PM

 

Post a Comment

Links to this post:

Create a Link

<< Home