|P is for Practical|
stephenby stephen (Priest)
|on Jan 22, 2000 at 13:47 UTC||Need Help??|
These days I work for NetApp as an Automation Architect. Previously, I worked at Yahoo! as a web tools developer.
In December 2004, I graduated from Carnegie-Mellon's West Coast campus at Moffett Field with a Master's in Software Engineering. CMU-West is a cutting-edge program (really!) that closely mimics an actual work environment. /msg me if you'd like to learn more about it.
On June 1st, 2002, a lovely woman named Christine Doyle and I got married.
I sing with the local a capella singing group Redshift.
I was an English major who found myself in programming through sheer accident. I've been programming Perl since 1995.
I'm gradually collecting refactorings-- recipes for improving code in small steps, without breaking existing code. See Refactoring for a general discussion of refactoring. This section is sort of a scratchpad; not all of these are as formally tested as I'd like. If you have refactorings you'd like to contribute, please feel free to send them here.
M.ost of the refactorings I collect here are Perl-specific refactorings-- problems that are most common in Perl, or for which Perl offers interesting solutions. For a list of general refactorings, see the Refactoring web site.
Condition: You have a scalar variable which is referenced in a double-quoted string without anything for it to interpolate with.
Resolution: Remove the double quotes.
is transformed to:
Condition: You have a global variable which is used in several different packages or files.
Resolution: Replace the global variable with a constant or subroutine that returns the same value.
is transformed to:
Perl has a construct that's well-designed for constants, it's called the 'use constant' pragma. Accessing a constant is on average just as fast as, or faster than, accessing a variable.
Things to watch out for
Condition: You have code that uses Perl's '&'-based default argument syntax.
Resolution: Make the arguments explicit.
is transformed to:
Putting explicit arguments into B makes it clearer what B is doing, plus makes the code more resilient.
Frequently, having many default arguments is a sign of an object waiting to happen.
Condition: Code duplicates functionality which is handled more robustly in a CPAN module.
Resolution: Replace the duplicated code with a call to CPAN code.
Wheels are good. Wheels have been around for a while. We've debugged the wheels. Many people know how wheels work, and how to fix them when they go flat.
Same thing applies to CPAN modules. CPAN modules make your code smaller and hence easier to hold in your head. They're generally well documented. They're generally supported by the developer or by a mailing list. Even so, pretty much every big project out there has code which duplicates the functionality of an existing, well-tested, well-supported CPAN module. It's best to replace that code.
CPAN modules are not necessarily perfect, though. There's plenty of alpha code in CPAN, and the code may not be well-tested for what you're trying to do with it. Look at the automated unit tests that come along with the module. If they don't cover the way you're trying to use the module, write your own tests. (It'd be an excellent idea to send them to the author for inclusion in future releases. That way, future releases of the module are less likely to break the functionality you need.) CPAN code should be as test-infected as the rest of your code. However, since CPAN code is a shared problem, it's likely to have reasonable tests defined already, making your job much easier.
In the particular, common case for the CGI code above, the end code is still not optimal. It's still using the old cgi-lib interface. One should apply "Update Interface" to move to the modern CGI interface. Although it's tempting to skip the compatibility interface and just charge into replacing everything with CGI::param calls, taking smaller steps reduces the risk that you'll have to abandon the refactoring.
Things to watch out for:
Condition: You have code that uses a deprecated interface to a module.
Resolution: Replace the deprecated interface with the more modern one.
However, these additional interfaces can get confusing. They can also preserve older-style constructions and habits, and make it harder to move ahead to well-factored, orthogonal code. Finally, such constructions are often an additional burden on the module maintainer, and may be dropped in future module releases. It's a good idea to update your code to use a module's primary interface.
With longer programs, it's important not to try to make all interface changes at once. Change the interface for a small section, test it, check it into CVS, and repeat. Once you've removed all of the old interface from a file or module, you can change the 'use' statement to avoid importing the functions that you no longer need. Make sure that you run tests afterwards.
CGI is a good example. The CGI module has a compatibility interface with the old cgi-lib.pl routines which make it easier to start using CGI in old code. Once you've updated to CGI, you can go through the code and update the interface in small steps. Once you think you've eliminated all of the code using the old ReadParse/%in interface, you can remove the ':cgi-lib' from the 'use CGI' declaration. Testing the code after that will tell you whether you've gotten rid of all of the old calls.
Condition: You have a series of if/then/elsif statements mapping one set of values to another
Resolution: Build a hash table mapping the two sets of values, and turn the set of if/thens to a subroutine lookup.
The idiomatic Perl solution is to use hashes any time one is mapping one set of values to another set of values. With a hash, it's easy to see what maps to what. Even better, you can put the hash at the top of your file, where it's easy to find and alter without messing around deep inside the code.
Build a table. Start at the top of your conditional block and add one line similar to michael => "knight" for each conditional. Then you can replace the conditional block with a simple hash lookup.
Refactorings by other users.
Other lists of refactorings: