Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I am now faced with integrating an inversion of control (IoC) container into an existing application, and I'm looking for some recommendations on how that can most easily be accomplished with the ultimate goal of reducing coupling, thereby increasing testability. Although I generally wouldn't classify most of the classes as god objects, each has too many responsibilities and hidden dependencies through statics, singletons, and lack of interfaces.

Here is a bit of background some of the challenges that need to be faced:

  • Dependency injection is infrequently used
  • Static methods abound - both as factory and helper methods
  • Singletons are fairly prevalent
  • Interfaces, when used, are not too granular
  • Objects often pull in unneeded dependencies through base classes

Our intent is that the next time we need to make changes in a particular area, that we try to tease out dependencies which, in actuality, exist but are hidden behind globals such as singletons and statics.

I suppose that makes the IoC container secondary to the introduction of dependency injection, but I would expect that there is a set of practices and recommendations that could be followed or considered that will help us break out these dependencies.

share|improve this question
3  
I have to ask on the reason for doing this now...what is driving this change? Maintainability? Scalability since it will grow exponentially? Developers are bored? –  Aaron McIver Jan 18 '11 at 16:43
1  
I just recently joined the company and am trying to introduce some "best practices." My main goal is to increase testability and reduce coupling. We can easily use DI/IoC for new code and don't intend to try to change all the existing code at once, but I'd like recommendations on how we can best change existing code for the better the next time we're making changes in that area. So far the only thing I've found online is the following: code.google.com/p/autofac/wiki/ExistingApplications –  Kaleb Pederson Jan 18 '11 at 16:51
3  
Unless there are large number of automated unit/integration tests in place; modifying the existing infrastructure unless a problem exists for the sake of best practices is asking for problems. Even if your unit/integration testing suite was solid I was still have hesitation. –  Aaron McIver Jan 18 '11 at 16:53
1  
@Aaron I hope it didn't sound like we're doing it for the sake of best practices. We're making changes because it's difficult and slow to work with the existing code and doing so piecemeal as we're working in that particular area. Gladly, we do have a set of reasonable integration tests and a few unit tests to support making changes. –  Kaleb Pederson Jan 18 '11 at 17:07

3 Answers 3

up vote 6 down vote accepted

In order to pull off something like this, you will have to work in steps, each one of these is not trivial but they can be accomplished. Before you begin, you have to understand that you cannot rush this process.

  1. Define the major subsystems of the application and an interface for each of them. This interface should only define the methods that other parts of the system will use to talk to it. NOTE: you may have to take more than one pass at this.
  2. Provide a wrapper implementation of that interface that delegates to the existing code. The purpose of this exercise is to avoid rewriting en mass, but to refactor the code to use the new interfaces--i.e. reducing the coupling in your system.
  3. Set up the IoC container to build up the system using the interfaces and implementations you created. At this stage you want to take care of instantiating the IoC container so that it can bring up the application. I.e. if you are in a servlet environment, make sure you can get/create the container in the servlet init() method.
  4. Do the same thing within each subsystem again, this time when you refactor, you are turning your stub implementation into the real thing which in turn uses interfaces to talk to it's components.
  5. Repeat as necessary until you have a good balance of component size to functionality.

When you are done, the only static methods you should have in your system are ones that are truly functions--for example look at the Collections class or the Math class. No static method should attempt to directly access other components.

This is something that will take a lot of time, plan accordingly. Make sure that as you perform your refactoring you are becoming more SOLID in your design approach. In the beginning it will be painful. You are drastically changing the design of your application iteratively.

share|improve this answer
    
Good suggestions. I refer others to the suggestions here as well when making such changes: code.google.com/p/autofac/wiki/ExistingApplications –  Kaleb Pederson Jan 19 '11 at 16:15

Pick up Working Effectively with Legacy Code and follow his advice. Start by building islands of covered code and gradually move toward a more well-structured application. Trying to make the change en masse is a recipe for disaster.

share|improve this answer
    
Love that book!! –  Martijn Verburg Jan 18 '11 at 17:44
    
Good recommendation. Although I read half of it years ago, I imagine I'd get much more out of it now that I'm waist deep in the situation. –  Kaleb Pederson Jan 19 '11 at 16:16
    
It's funny, the selected answer basically summarizes the book ;) Of course Feathers goes into much more detail. –  Mike Brown Jan 19 '11 at 17:42

The primary reason for introducing IoC is decoupling of modules. The problem with especially Java is the extraordinary strong binding that the new operator gives, combined with that it means that the calling code knows exactly what module it will use.

Factories were introduced to move this knowledge to a central place but in the end you still either hardwire the modules in using new/singleton which keeps the hard binding, or you read in a configuration file and use reflection/Class.forName which is brittle when refactoring.

If you do not have the target modularized then IoC is not going to give you anything.

Introducing Unit tests will most likely change this, as you will need to mock out modules which are not under test, and the easiest way to handle this as well as actual production modules with the same code is to use an IoC framework to inject the appropriate modules.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.