Book Review: Refactoring
![Refactoring [amazon.com]](/reviews/images/refactoring.jpg) |
Title: Refactoring - Improving the Design of Existing Code
Author: Martin Fowler
Publisher: Addison-Wesley
On-line: amazon.com amazon.co.uk
Reviewed by
July 27, 2003 |
If you're a programmer, it's almost a dead-cert that at some point you have had to make a change to someone else's code. It's also likely that your reaction when trawling through the code was one of abject horror: "What was he thinking when he wrote this 700-line function?"
Whatever the cowboy coder was thinking about, it would have had little to do with maintainability. Attempting to change the code in that state will almost certainly introduce new bugs. It's likely that the cowboy coder was thinking about getting his code working - hacking it to the point where it works - so that he could move on to his next grand masterpiece.
It's this kind of scenario where refactoring can prove to be very useful. It's a fairly safe bet that if you spend a little time carefully tidying up the cowboy code before you start adding to it, you'll save a lot of time bug-fixing later on.
Refactoring is the practice of improving code without changing its meaning. It's the cornerstone (okay, one of the cornerstones) of Extreme Programming (XP). That's not to say that you have to be doing XP to do refactoring. The refactorings described in Martin Fowler's book are equally useful in a non-XP context.
Refactoring is important because it places a big emphasis on making code readable and simple - basically self-documenting.
For example (not actually from the book), a code generator application might include a Java method like the following:
/**
* Generate property accessor methods. Populates target with generated text.
* Rewrites source text based on the field details.
*/
public void populateBuffer(StringBuffer target, char[] source, String fieldBasePropKey, String fieldType, Object sourceBean, int mode, boolean isBound) { ... }
A few things spring to mind without even bothering to look at the method body. The first issue is the existence of a code comment describing what the function is there for. That in itself is a warning sign that the code isn't clear enough to be self-explanatory. Another issue is the number of parameters - and the way in which they reveal way too many implementation details. What we'd like to end up with is something like this:
public String generatePropertyAccessors(String template, Field field) { ... }
Much better! As you can see, the result is now a return value - much more "natural" in Java (and almost every other language!); the method name describes what the method does, rather than how it's achieving it (what vs. how); and the multitude of weird parameters have been encapsulated into a single Field object. Notice also that we just don't need the code comment any more, because it's plain obvious what the method does. So that's even less that we have to maintain.
|
"Several Short Baby Steps" |
|
Using the refactoring approach described in Fowler's book, getting to this new improved method signature would involve several short "baby steps". Each step is part of a pattern-style refactoring - the above example would use Rename Method and Introduce Parameter Object, plus no doubt several more to refactor the method body.
Further examples from the book include: Pull Up Constructor Body, Move Method, Replace Type Code With Subclasses, Replace Temp With Query, and so on. In fact, the bulk of Refactoring (the book) consists of these patterns (or recipes, or refactorings).
Each refactoring describes a series of short steps you would carry out in order to make the change to your code. The emphasis at every step is on making the change without breaking existing functionality. The refactorings build on classic design patterns as described in the book Design Patterns [amazon.com amazon.co.uk]. As such, Refactoring can be thought of as "design patterns in motion".
Unit Tests - An Essential Ingredient
In any refactoring, each "baby-step" involves running a complete suite of unit tests to make sure the change hasn't broken any part of the application. This is a fundamental requirement of refactoring - any modifications made to existing code need to be backed up with a thorough set of unit tests. This is true even if the existing code was only written this morning.
From Fowler's book (p.7):
"Whenever I do a refactoring, the first step is always the same. I need to build a solid set of tests for that section of code. The tests are essential because even though I follow refactorings structured to avoid most of the opportunities for introducing bugs, I'm still human and still make mistakes. Thus I need solid tests."
This might seem like a major overhead - taking this approach, you will typically end up with one and a half times as much test code as "real" code. But unit tests have their own benefits, and the effect on code quality can be dramatic. Unit tests encourage you to delineate your code better - e.g. separating presentation from business logic, so that the business logic can be tested more readily. (Note that a decent up-front design process also has the same effect). Unit tests also tend to catch the more obscure bugs that probably would have lain unnoticed until the program was released and the users start entering all sorts of unpredictable data.
Limitations of Refactoring
Unit tests combined with refactoring are a useful combination. However, there is a law of diminishing returns with refactoring - and this is a limitation that Fowler's book does not really go into. Basically, if the code you have written is finished, passes all its tests, has passed through the hands of QA, and is not likely to be extended any further (at least for this iteration of the project) then there's really nothing to be gained from spending more time refactoring it. A documented architecture would help to determine the exact point when refactoring becomes a burden rather than a benefit. (This is something which I describe in more detail in my book Extreme Programming Refactored, co-authored with Doug Rosenberg).
|
"Changing interfaces can cause problems unless the team is really on-the-ball" |
|
Another limitation (which is covered in Refactoring, in the section "Changing Interfaces") is that most of the refactorings involve interface changes. In a team environment, this can cause problems (out-of-date code, broken client code etc) unless the team is really on-the-ball and aware of such problems, unfailingly throughout the project. This is less of a problem with occasional refactoring, but becomes an issue in a methodology such as XP that promotes constant refactoring in the context of emergent design.
Refactoring does have a few paragraphs describing two scenarios when you shouldn't refactor. The first is when it would be easier to simply rewrite from scratch (this is a sort of uber-refactoring, so still achieves the goal of refactoring, just in a different way - so it doesn't really qualify as a scenario when not to refactor, per se). The second scenario is when you're close to a deadline, and the benefit of the refactoring would not appear until the next iteration - so better to do the refactoring in the next iteration (if there is one).
So the two cases when not to refactor (as given in Fowler's book) are: 1) when you want to really refactor, i.e. completely rewrite the code; and 2) when you want to do it later instead. It strikes me that there are a lot more scenarios when refactoring isn't appropriate (like the other ones described above).
A minor quibble I have with the book is that Fowler (especially in the sections co-contributed by Kent Beck) talks a lot about "bad code smells". Eeeuww. I really wish they'd found a different way of describing "danger signals" in code!
Conclusion
Overall, this book is a must-read for programmers everywhere - beginners and advanced alike. Experienced programmers may read some of the prescribed refactorings and think, "Well, duhh, obviously!" but the overall lesson definitely reinforces good practices that may have been forgotten, suppressed, or never learnt in the first place.
The pattern-based approach also helps to crystallise a lot of the good practices in such a way that they stick around in the mind long after you've read the book. You might forget the specific names of each refactoring, but the good habits should remain. Reading this book will make you a better programmer!
More book reviews:
Use Case Driven Object Modeling With UML: A Practical Approach (Doug Rosenberg, Kendall Scott)
The Inmates Are Running the Asylum (Alan Cooper)
Message Forum:
Agree/disagree with this review? Please add your comments below!
Post a new message
Message Index: Time factor Nanik nanikjava@yahoo.com
re: Time factor Matt Stephens
"Bad code smells" Peter Frost peterxfrost@yahoo.com
Mirror Image Mark Baldridge reiki33@yahoo.com
The Messages: Time factor Hi,
I think refactoring will be of benefecial if there is not time constraint imposed on the project (which I'm sure not all projects have got the luxury). I've been into so many projects and when trying to change and refactor code it is almost impossible to do it, because of time constraint that we are put against us.
So in a real world environment, refactoring are applicable IF and only IF we have enough time to do it otherwise we need to really make a good design before coding anything.
Cheers Nanik
Nanik nanikjava@yahoo.com Indonesia Fri Sep 12 03:16:24 BST 2003
re: Time factor Hi Nanik,
The time factor does play a part in deciding whether or not to refactor. A lot depends on how much more a piece of code needs to be extended or modified. If some code is badly written, refactoring it before making changes can save a lot of time.
But if some code is already well written (i.e. simple, modular, maintainable), or if the code is near the end of its lifecycle, then it's probably not worth refactoring.
It's a judgement call, as with many things in life... Matt Stephens London, England Sun Sep 14 09:47:24 BST 2003
"Bad code smells" Hmmm, I would look for other reasons for this stench. May be it has to do with foul air in over-crowded rooms?
Peter Frost peterxfrost@yahoo.com CA, USA Mon Sep 29 18:55:38 BST 2003
Mirror Image I positively resonated with this book, almost as much as Kent Beck's Smalltalk Best Practices.
What amused me, was that half the book describes how to covert code from one pattern to another. Because situations dictate that one pattern does not fit all needs, the other half the book describes how to morph code from the other pattern to one in the first half!
And this from someone who has a emulator for the IBM 370, running in Java, with hundreds of JUnit tests to verify the machine. Mark Baldridge reiki33@yahoo.com MA, USA Sat Mar 06 04:39:22 GMT 2004
Post a new message
<< Back to Reviews
<< Back to the Front Page
|