Wednesday, September 3, 2008

Refactoring




    1. Introduction

Refactoring is the process of restructuring code using a disciplined technique.




    1. Benefits

The benefits of refactoring include:



  1. more maintainable


  1. easier to read and understand


  1. easier to modify/add new features

Refactoring plays an important role in Extreme Programming (XP) where aggressive refactoring is encouraged.




    1. Unit Testing

Unit testing is a crucial component of refactoring.


1) Make sure the code being refactored has sufficient unit tests.


2) Make sure the code passes the unit tests.


3) Refactor.


4) Re-run the unit tests.




    1. When to Refactor

Guidelines indicating when to refactor include:



  1. When doing a code review.


  1. When adding new functionality.


  1. When bug-fixing.


  1. Whenever you have trouble understanding code.



    1. Smelly Code

Martin Fowler refers to code smells for identifying when to refactor. Some of these include:


1) Duplicated code. Extract out the common code into a method.


2) Long methods. Break up some of the code into separate methods.


3) Shotgun surgery - changes in one class require making changes in several others. Try to move all related pieces of code into a single, cohesive class.


4) Poor names - Rename data and methods to something more meaningful.




    1. Rules


  • Extract Method

You have a code fragment that can be grouped together. Turn the fragment into a method whose name explains the purpose of the method.



    void PrintOwing()



    {


PrintBanner();


//print details


System.Console.WriteLine("name:" + _name);


System.Console.WriteLine ("amount" + getOutstanding());


}







void PrintOwing()


{


PrintBanner();


PrintDetails(getOutstanding());


}


void printDetails (double outstanding)


{


System.Console.WriteLine ("name:" + _name);


System.Console.WriteLine ("amount" + outstanding);


}



  • Pull Up Method


    You have methods with identical results on subclasses. Move them to the superclass.





  • Rename Method


    The name of a method does not reveal its purpose. Change the name of the method.






  • Decompose Conditional


    You have a complicated conditional (if-then-else) statement. Extract methods from the condition, then part, and else parts.



    if (date.before (SUMMER_START) date.after(SUMMER_END))



    charge = quantity * _winterRate + _winterServiceCharge;



    else



    charge = quantity * _summerRate;






    if (notSummer(date))



    charge = winterCharge(quantity);



    else



    charge = summerCharge (quantity);



  • Remove Double Negative


    You have a double negative conditional. Make it a single positive conditional



    if ( !item.isNotFound() )






    if ( item.isFound() )



    Motivation



    Double negatives are often frowned on by mavens of natural language. Often this frowning is inappropriate - certainly in English the double negative has its uses.



    But that is not true in programming. There double negatives are just plain confusing. So kill them on sight.



    Mechanics




    • If you don't have a method with the opposite sense, create one. The body can just call the original (you can fix that later).

    • Compile

    • Replace each double negative with a call to the new function

    • Compile and test after each replace

    • If the negative form of the method isn't used elsewhere, use private method to inline it into the positive form



  • Extract Interface


    Several clients use the same subset of a class's interface, or two classes have part of their interfaces in common. Extract the subset into an interface.






    1. More Rules


  • Unit Testing: Don't Even Start Without It. You won't know your refactoring didn't break the code unless you have unit test coverage of the affected code. If you don't have that coverage already, add new tests.


  • Keep Each Refactoring Narrowly-Focused. Don't combine unrelated changes, refactor each one separately.


  • Keep Your Unit Tests Fine-Grained. If a refactoring involves multiple functions, you're best off having unit tests for each function. If you depend on a single unit test for a function that then calls the refactored functions, a test failure leaves you not knowing which refactored function failed.


  • Use Many Small Refactorings, Even When It Seems Inefficient. I prefer making a small refactoring even when I know that a subsequent refactoring will change that same code yet again. This is critical for refactoring tangled, poorly-written code.


  • Unit Test Each Refactoring. Don't wait.


  • Refactor Separate Functionality Into Separate Functions. If I encounter code that combines too much distinct functionality into one function, I try to break it apart into separate, individually-testable functions. With unit tests for each new function.



    1. Refactor Mercilessly (Extreme Programming approach)


We computer programmers hold onto our software designs long after they have become unwieldy. We continue to use and reuse code that is no longer maintainable because it still works in some way and we are afraid to modify it. But is it really cost effective to do so? Extreme Programming (XP) takes the stance that it is not. When we remove redundancy, eliminate unused functionality, and rejuvenate obsolete designs we are refactoring. Refactoring throughout the entire project life cycle saves time and increases quality.


Refactor mercilessly to keep the design simple as you go and to avoid needless clutter and complexity. Keep your code clean and concise so it is easier to understand, modify, and extend. Make sure everything is expressed once and only once. In the end it takes less time to produce a system that is well groomed.


There is a certain amount of Zen to refactoring. It is hard at first because you must be able to let go of that perfect design you have envisioned and accept the design that was serendipitously discovered for you by refactoring. You must realize that the design you envisioned was a good guide post, but is now obsolete.


A caterpillar is perfectly designed to eat vast amounts of foliage but he can't find a mate, he must refactor himself into a butterfly before he is designed to search the sky for others of his own kind. Let go of your notions of what the system should or should not be and try to see the new design as it emerges before you.


No comments: