views:

468

answers:

7

I'm trying to refactor a big tightly coupled application and trying to make it more maintainable and flexible.

I've got many unit tests, so I'm hoping to refactor step by step.

Which Design & Refactoring Patterns should I consider implementing / applying to accomplish this task ?

I can think of some :

Also feel free to share your own experience and best practices for this kind of refactoring job.

UPDATE

I'm carrying out this refactoring because of the reasons explained in this question. Basically I can't implement a plugin system without extracting an couple of interfaces and those interfaces are highly coupled which requires separated the application in 40+ DLLs to just compile without circular referencing problem.

A: 

Carefully.

Jeff Yates
If the unit tests he has have good coverage, carefully should be easier to do.
Ben S
:) Well, that's good point but not helping much now, is it? Test coverage is not awesome, but it's decent. So should be all right.
dr. evil
+5  A: 

In all seriousness, refactoring is not an undertaking to be taken lightly, especially in tightly coupled systems. It can often seem like a worthwhile task before it has been performed, but from my experience, it can soon become a burden once started as it is often more likely to introduce new bugs than solve any existing issues.

Before embarking on a major refactoring, you should consider what the gains will be and what alternatives there are (such as creating a new product from scratch, refactoring only the parts that need it right away, etc.). You should certainly have a good understanding of the architecture, roles, and responsibilities before starting, as well as the expected and existing behavior to ensure that you know when you've broken something.

Also, it can be beneficial to draw up a design of how it will be after the refactoring and how that maps to the current implementation so that you can stay on point. You should also regression test as often as possible.

It can be frustrating to the perfectionist when a design is quite obviously in need of refactoring, but sometimes one has to consider the cost/benefit of the changes and concede the battle. If you must make the changes, tread carefully and don't try to do too much at once.

Jeff Yates
@Jeff I added an update to the question explains that why I'm doing this.
dr. evil
To be honest if there is a way to avoid it I'm planning to avoid it at least for another couple of months as I'm in rush with this app. However looks like I have to decouple classes to implement a plugin system.
dr. evil
+3  A: 

Refactoring to interfaces and dependency injection are going to be key in reducing coupling. You might also want to consider using factories for the creation of your dependent objects.

tvanfosson
+6  A: 

This is a big question, and one could write an entire book in answer to it. Fortunately, someone already has. Grab yourself a copy of Working Effectively with Legacy Code by Michael Feathers. It's pretty much an entire book devoted to answering your question.

It's a really good book, too. I'd definitely put it up there with Code Complete, Design Patterns, and Pragmatic Programmer on a list of books that should be in every developer's library.

Adam Jaskiewicz
I've read Code Complete and Pragmatic Programmer. Currently looking into Working with Legacy Code, looking got an entire section devoted to Dependency-Breaking techniques.
dr. evil
Yeah, it does. From the back of the book: "Topics covered include... improving design... getting legacy code into a test harness... writing tests that protect you against introducing new problems...handling applications that don't seem to have any structure... catalog of twenty-four dependency-breaking techniques."
Adam Jaskiewicz
+1  A: 

All of the above and then some. But before you start any of that, I'd think about the big picture. Define the sections of your program (packages, projects, etc) and then have a plan for moving functionality around to be in the appropriate package. Then once everything is where it logically should be start using extract interface, dependency injection, and factory methods to start de-coupling the packages.

C. Ross
A: 

Some great advice from Phlip: Refactor the Low-hanging Fruit

I think it's very hard to say which specific refactorings are appropriate in your specific case without a great deal more information.

Carl Manaster
A: 

Thanks for all answers, After struggling with so many different ways I found that the best thing to do was creating interfaces for everything. This allowed me to change the design freely and I only break the build for a day (a day because the project was big and I need to fix so many references and unit tests + some refactoring).

After day extracting and fixing all interfaces I can create separate solutions and play with the design freely.

Basically first move I think should be moving everything to interfaces and then try to get rid of internal dependencies.

dr. evil