One approach is to seek the simplest design. Kent Beck has identified four rules for simple design; if your code violates these rules (which are in priority order), then you have a problem to address.
Runs all the tests.
Has no duplicated logic. Be wary of hidden duplication like parallel class hierarchies.
States every intention important to the programmers.
Has the fewest possible classes and methods .
”Beck, Extreme Programming Explained , p. 57
A shorthand name for these rules is OAOO, which stands for once and only once . The code has to state something once so that it can pass its tests and communicate the programmer's understanding and intent. And it should say things only once, i.e., with no duplication.
It's hard to clean up code that hasn't been kept clean; few teams can afford to turn the lights out for months on a quest for perfection . But we can learn to make our code better during development, and we can add a little energy each time we're working in an area.
E XTREME P ROGRAMMING (XP) AND R EFACTORING
Extreme Programming is an agile software development method, characterized by a particular planning style, a team focus, and programming through tests and refactoring. While refactoring has been around longer, there's a cluster of people ”including Kent Beck, Ward Cunningham, Martin Fowler, and Ralph Johnson ”who have been working in the areas of object-oriented development, patterns, refactoring, and other related ideas.
Programming in XP is built around three things: an ethos of simple design, test-first programming, and refactoring. Test-first programming is an approach that says, "Write a test, see it fail, write code, see the test pass." When you put all of these together, you get what is now called test-driven development . See Test-Driven Development: A Practical Guide by Dave Astels and Test-Driven Development: By Example by Kent Beck for a deeper description of this technique.
I'm a fan of the test-driven approach (and XP, for that matter), but the discipline of refactoring doesn't require it. However, code created this way will typically have fewer errors and will need less of the big refactoring that typical code requires. In particular, the bigger examples in the last half of this book would be much smaller and less smelly if they'd been done using test-driven development.
T HE E NVIRONMENT FOR R EFACTORING
Refactoring can be done with just a simple text editor, but refactoring is easier and safer with a supportive environment.
Team or Partner: For nontrivial decisions about code, it's helpful to have more than one person considering the problem. A team can often generate ideas better than one person alone: Different people have different experiences and different exposure to different parts of the system.
Tests: Even though refactorings are designed to be safe, it's possible to make a mistake in applying them. By having a test suite that is run before and after refactoring, you help ensure that you change the design of your code, not its effects.
What if you don't have tests? Then add them, at least to the areas affected by the refactoring. Sometimes this is tricky ”you may be unable to test effectively without changing the design, and yet it's unsafe to change the design without tests. And areas that are tricky to test often indicate other problems in the design.
"If you want to refactor, the essential precondition is having solid tests."
” Martin Fowler, Refactoring, p. 89
Testing Framework: The JUnit test framework ( www.junit.org ) has become a standard. The later examples use it. Test classes extend the TestCase class, and they contain methods whose names start with the word test . The framework provides a number of assert methods that let you verify the code's behavior. It also comes with a test runner that can run a suite of tests.
CRC Cards or UML Sketches : Refactoring doesn't mean eliminating design. Sometimes you may hold a Class-Responsibility-Collaborator (CRC) card session or draw Unified Modeling Language (UML) sketches to compare alternatives for refactoring.
Configuration Management/Version Control: This can range from Undo to a full-fledged configuration management system. If you make a mistake while refactoring, you'd like to be able to return to the last known good point. Alternatively, you may want to apply a refactoring, but you may not be sure if the result will be an improvement. It's helpful to be able to try it and then decide whether to keep the result.
Sophisticated Integrated Development Environment (IDE): Simple, but powerful, languages such as Smalltalk and Lisp have had refactoring support available in their environments for a number of years . Java has had such support only for the last year or two, and refactoring support is just now beginning to show up for C#. The user still has to decide which refactoring to apply, but the tool removes a lot of the error-prone tedium.