Re[2]: Suggestions required for Refactoring.
Michael Feathers <mfeathers <at> mindspring.com>
2004-06-01 13:41:03 GMT
OW> I've had a similar experience, taking over a C++ codebase when starting a
OW> new job. Fowler strongly recommends refactoring only when you already have
OW> unit tests, to prove that you have preserved behavior. However, if you have
OW> no unit tests to start with, then any change is likely to introduce bugs,
OW> whether they be refactorings or just maintaining the code. Often it is
OW> necessary to refactor before you can add unit tests, and these "unsafe"
OW> steps towards a better design and testing framework have a lot of value. For
OW> instance, if you're in a situation where data access code is mingled with
OW> business logic, it can be very difficult to write unit tests.
OW> A period of unsafe refactoring can separate the business logic and data
OW> access allowing the tests then to be written. The point is you *will*
OW> introduce bugs. If you're writing safety critical software or you're risking
OW> millions of dollars on this technique, I'd take another path. Do try and
OW> peform some functional tests as you go, as you'll catch any major flaws as
OW> you go. Do use a source control tool such as CVS so if you really go down
OW> the wrong path you can recover to a working version.
The thing to recognize is that different projects have different risk
profiles. Not all projects are safety critical or have large amounts
of money in play.
I advocate higher level tests when you can get a sense of what they
cover, but in general the further tests are from what they test, the
harder it is to determine that and you may think that they are
preserving the behavior you want to change when they aren't.
There are some refactorings you can do safely without tests, but you
have to be *very* conservative. Here's one. Try to think of all of
the things that can go wrong when you do this refactoring. There
aren't too many.
I have a class which accesses a singleton logger in a method:
class RTYDispatcher : public Dispatcher
{
public:
void dispatch(COMPacket *packet) {
...
Logger::getInstance()->logMessage(INFORMATIONAL, "packet sent");
...
}
};
I want to see that the packet was sent in a test, but without
instantiating and checking a logger.
Here's what I can do.
Find the declaration of the logMessage method of Logger:
void logMessage(int severity, const string& message);
Copy it's signature into the RTYDispatcher class as a protected
method:
class RTYDispatcher : public Dispatcher
{
public:
void dispatch(COMPacket *packet) {
...
Logger::getInstance()->logMessage(INFORMATIONAL, "packet sent");
...
}
private:
virtual void logMessage(int severity, const string& message);
};
Copy the logging like from the dispatch method into a body for the new
method:
void RTYDispatcher::logMessage(int severity, const string& message)
{
Logger::getInstance()->logMessage(INFORMATIONAL, "packet sent");
}
Adjust the parameters:
void RTYDispatcher::logMessage(int severity, const string& message)
{
Logger::getInstance()->logMessage(severity, message);
}
Now go back to the dispatch method and remove the text
"Logger::getInstance()->":
class RTYDispatcher : public Dispatcher
{
public:
void dispatch(COMPacket *packet) {
...
logMessage(INFORMATIONAL, "packet sent");
...
}
private:
virtual void logMessage(int severity, const string& message);
};
Now you can subclass RTYDispatcher and override logMessage to sense
what is sent to it.
This refactoring required minimal editing and, critically, it
'preserved signatures' so there isn't a chance of a conversion error.
If you have a tool that does safe extract method, this refactoring is
even more direct.
The trick is, working with a partner, working small, and be very
conservative. Most errors I've run into using these techniques are
the result of too much ambition, just do what it takes to break
dependencies that prevent testing.
Michael Feathers
www.objectmentor.com
------------------------ Yahoo! Groups Sponsor --------------------~-->
Yahoo! Domains - Claim yours for only $14.70
http://us.click.yahoo.com/Z1wmxD/DREIAA/yQLSAA/umvwlB/TM
--------------------------------------------------------------------~->
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/refactoring/
<*> To unsubscribe from this group, send an email to:
refactoring-unsubscribe <at> yahoogroups.com
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/