Have you tried my refactoring challenge[1]? Since I missed two days in my first week of the 100 Pushups challenge, I guess I can’t complain if you haven’t tried refactoring. Here’s another example that might prompt you to try it. (Refactoring, not push-ups.)
I’m preparing a simple utility so the code is stranger-stable. In prepping the code, the following lines gave me pause.
SaveOldCommand()
WriteCommand( lcNewCommandPrg )
When I have to stop, even briefly, and ask what code is doing, or worse, if I have to divert into another method, I smell a refactoring. At first, I read the code as “SaveCommand, WriteCommand.” Wha..aa? The “Old” in SaveOldCommand() explains it, but it’s buried in the middle of the function name. Similarly the explicitly named variable lcNewCommandPrg passed to WriteCommand is good, but, it comes at the end of the line. I can refactor this code to make it clearer. To do that I used Rename Method. (Whether or not this is the most compelling example, isn’t the point, you kibbitzers out there.)
In a nutshell, renaming a method starts by creating a new method with the new name. Then move the old method’s code into the new method. Finally, change existing calls to the new method. Stay with me: it works on legacy code, too. More on that later. Here are the steps:
- Test the code (baseline, right?)
- Create a new method with the new name, with the same parameters, and test.
- Copy the code from the old method into the new one, and test.
- Find all references to the old method and change them, one at a time, to the new method.
- Test after every change in step 4.
Yes. Step 4 is a good example of “And then a miracle occurs” programming. But, no fears, this technique is adaptable to more difficult situations. In this case, however, I know it’s safe to remove the old method. After I refactor, the code is:
BackupOld()
WriteNew( lcNewPrg )
It’s usually impractical to rename a method other than in early development, isolated utilities, or hidden methods. Legacy code is too hard to search thoroughly and is rarely testable with the degree of assurance that’s required. If code is a control or application that is used by customers I cannot change method names at all. However, I can safely create new methods and change the old ones to call the new.
Consider the following changes to the steps above:
- Test the code.
- Use Extract Method to move the code from the old method to a new, better named method, and replace the old code with a call to the new method. Test.
- Change existing references to the old method, testing after each change for as long as you can stand it.
- Test.
“What’s the point of just adding another line of code to execute,” you ask? Consider a class that you will continue to use, or a class that customers use. Even if old code still calls the new method, a more clearly named method will be easier to work with, and the code that calls it will be more readable. Also, you might use BindEvent (VFP) to bind the old method to the new.
For example, years ago I adapted a Visual FoxPro foundation label class to send email. The original class used a method named something unhelpful. I’d added hooks so instances and subclasses could manipulate the email before it was sent. As I used the class in different projects, I wanted a more clearly named method, and so created a SendMail method.
The trouble with removing the old method was that I used the class in several different projects, and while I could have used Agent Ransack to find code references (this was in VFP 7.0, which doesn’t have Code References), I didn’t have time to find, replace, and test all the changes needed. Plus, I really just wanted to make it easier to use in the future. Otherwise it worked just fine as it already existed. So, I chose to the second refactoring and used Extract Method.
Why do I mention this? I want to convince the skeptic that refactoring is flexible, doesn’t require a massive investment in time, and leaves code better but still stable. I’ll keep coming back to these facts time and again. I can’t imagine why anyone wouldn’t want to refactor whenever possible. But then, I can’t imagine why anyone would eat lima beans willingly, either.
[1]No, I’m not really delusional about the size of my audience, its interest in refactoring, or my powers of persuasion. I just like to pretend.