Refactoring - improve the code and make it understandable - Part 1

A coder can write code, it will work and will do the job. So writing a code would be pretty straight forward. I found many coder, after finishing their code they want to optimize the code. Then the concept comes REFACTORING.

Now a days many IDEs for example Microsoft Visual Studio 2010 support Refactoring. So it will refactor the code on behalf of the  coder by clicking few options.  For example, in VS 2010 if we click on the Refactor menu item we will see the following menu items:


Refactor
  • Rename
  • Extract Method..
  • Encapsulate Field...
  • Extract Interface...
  • Remove Parameters...
  • Reorder Parameters....

as well as there are many books in regards to the refactoring. I like the book named, Refactoring: Improving the Design of Existing Code by Martin Fowler. I think if we get chance we should read this book.

In this article I am going to write few refactoring methods.

Extract Class - Refactoring
The main objective of this refactoring is related members from one class to another independent class. In this following example Person class has Name, OfficeAreaCode and OfficeNumber. Using this extract class refactoring strategy I am going to move  OfficeAreaCode and OfficeNumber members from Person class to TelephoneNumber class. If we see the class diagram then perhaps it would be more clear.


Fig: Extract class.

Person class before refactoring,

namespace RefactoryExampleLibrary.ExtractClass
{
    public class Person
    {
        public string Name { getset; }
        public string OfficeAreaCode { getset; }
        public string OfficeNumber { getset; }
 
        public Person() { }
 
        public string GetTelephoneNumber()
        {
            return string.Concat(new[] { OfficeAreaCode, OfficeNumber });
        }        
    }
}


After applying the Extract class Refactoring method Person class has been divided into two separate classes.

namespace RefactoryExampleLibrary.ExtractClass
{
    public class PersonAfter
    {
        public string Name { getset; }
        public TelephoneNumber officeNumber;
 
        public PersonAfter()
        {
            officeNumber = new TelephoneNumber();
        }
        public string GetTelephoneNumber()
        {
            return officeNumber.GetTelephoneNumber();
        }
    }
}

namespace RefactoryExampleLibrary.ExtractClass
{
    public class TelephoneNumber
    {
        public string OfficeAreaCode { getset; }
        public string OfficeNumber { getset; }
 
        public TelephoneNumber() { }
 
        public string GetTelephoneNumber()
        {
            return string.Concat(new[] { OfficeAreaCode, OfficeNumber });
        }
 
    }
}




The test class Program will test the above code.

namespace Refactoring_TestHarness
{
    using System;
    using RefactoryExampleLibrary.ExtractClass;
 
    class Program
    {
        static void Main(string[] args)
        {
 
            Person person = new Person()
            {
                Name = "A",
                OfficeAreaCode = "02",
                OfficeNumber = "1111"
            };
            Console.WriteLine("{0}", person.GetTelephoneNumber());
 
            PersonAfter personAfter = new PersonAfter()
            {
                Name = "A",
                officeNumber = new TelephoneNumber()
                {
                    OfficeAreaCode = "02",
                    OfficeNumber = "1111"
                }
            };
}


The output of the above code will exactly same as below,

021111
021111
Press any key to continue . . .


Introduce Parameter Object - Refactoring
The motive of this method is to reduce long parameter list from a class method. For example following GetDateOfBirth method of IntroduceParameterObject is taking three parameter for instance, day, month, year. So instead of passing three parameter we could reduce this to one parameter by implementing one type class such as DobComponents in here.
end code
namespace RefactoryExampleLibrary.IntroduceParameterObject
{
    public class IntroduceParameterObject
    {
        public IntroduceParameterObject() { }
 
        public string GetDateOfBirth(string day, string month, string year)
        {
            string[] dobComponents = { day, month, year };
            return string.Join("/", dobComponents);
        }
        public string GetDateOfBirth(DobComponents components)
        {
            string[] dobComponents = { components.Day, components.Month, components.Year };
            return string.Join("/", dobComponents);
        }
    }
 
    public class DobComponents
    {
        public string Day { getset; }
        public string Month { getset; }
        public string Year { getset; }
 
        public DobComponents() { }
    }
}

namespace Refactoring_TestHarness
{
    using System;
    using RefactoryExampleLibrary.ExtractClass;
    using RefactoryExampleLibrary.IntroduceParameterObject;
 
    class Program
    {
        static void Main(string[] args)
        {
            IntroduceParameterObject introduceParameterObject = new IntroduceParameterObject();
            Console.WriteLine("IntroduceParameterObject output,");
            Console.WriteLine("{0}", introduceParameterObject.GetDateOfBirth("01""01""0000"));
            Console.WriteLine("{0}", introduceParameterObject.GetDateOfBirth(new DobComponents() { Day = "01", Month = "01", Year = "0000" }));
}
}


The output of the class is as below,

IntroduceParameterObject output,
01/01/0000
01/01/0000
Press any key to continue . . .


Preserve Whole Object - Refactoring
This sort of refactoring reduce local variable dependency of the data and as well delegate the responsibility to the relevant type. In this example, CheckDateRangeWithoutPReserveObject method use Temperature type to get High and Low temperature by storing locally by declaring high and low local variable. Instead of doing this, leave the responsibility to the relevant method to access value. For instance, in here I implement a method name TemperatureWithDays with input parameter type Temperature so then this method can directly access high and low value from the Temperature object. 

namespace RefactoryExampleLibrary.PreserveWholeObject
{
    using System;
    public class PreserveWholeObject
    {
        public PreserveWholeObject() { }
        /*
         * Before Preserve Whole Object
         */
        public bool CheckDateRangeWithoutPreserveWholeObject()
        {
            Temperature temperature = new Temperature();
            int high = temperature.GetHigh();
            int low = temperature.GetLow();
 
            return new DaysTemperature().TemperatureWithDays(high, low);
        }
        /*
         * After Preserve Whole Object
         */
        public bool CheckDateRangePreserveWholeObject()
        {
            return new DaysTemperature().TemperatureWithDays(new Temperature());
        }
    }
 
    internal class DaysTemperature
    {
        public int todayTemperature = 250;
        public DaysTemperature() { }
 
        public bool TemperatureWithDays(int high, int low)
        {
            return (todayTemperature < high && todayTemperature > low);         
        }
 
        public bool TemperatureWithDays(Temperature temperature)
        {
            return (todayTemperature < temperature.GetHigh() && todayTemperature > temperature.GetLow());
        }
    }
 
    internal class Temperature
    {
        public Temperature() { }
 
        public int GetHigh()
        {
            return Int16.MaxValue;
        }
 
        public int GetLow()
        {
            return Int16.MinValue;
        }
    }
}

namespace Refactoring_TestHarness
{
    using System;
    using RefactoryExampleLibrary.PreserveWholeObject;
 
    class Program
    {
        static void Main(string[] args)
        {

            PreserveWholeObject preserveWholeObject = new PreserveWholeObject();
            Console.WriteLine("PreserveWholeObject output:");
            Console.WriteLine("{0}", preserveWholeObject.CheckDateRangeWithoutPreserveWholeObject());
            Console.WriteLine("{0}", preserveWholeObject.CheckDateRangePreserveWholeObject());
        }
}


The output of above code will be as below,

PreserveWholeObject output:
True
True
Press any key to continue . . .


Replace method with method object - Refactoring
Again in here the whole concept is to de-couple logic to the relevant type instead of having in the caller. In this example, I extract the computation logic from the Gamma method's to the Gamma class and call this new compute method of Gamma class from Gamma method.

namespace RefactoryExampleLibrary.ReplaceMethodwithMethodObject
{
    using System;
    public class ReplaceMethodwithMethodObject
    {
        public ReplaceMethodwithMethodObject() { }
 
        public int ComputeGamma()
        {
            return new Account().Gamma(1, 1, 1);
        }
 
        public int ComputeGammaRefactored()
        {
            return new Account_Refactored().Gamma(1, 1, 1);
        }
    }
 
    /*
     * Before Refactoring
     */
    public class Account
    {
        public Account() { }
        public int Gamma(int inputVal, int quantity, int yearToDate)
        {
            int importantValue1 = (inputVal * quantity) + delta();
            int importantValue2 = (inputVal * yearToDate) + 100;
 
            if ((yearToDate - importantValue1) > 100)
                importantValue2 -= 20;
 
            int importantValue3 = importantValue2 * 7;
            // and so on.
            return importantValue3 - 2 * importantValue1;
        }
 
        public int delta()
        {
            return 1;
        }
    }
    /*
     * After Refactoring
     */
 
    public class Account_Refactored
    {
        public int Gamma(int inputVal, int quantity, int yearToDate)
        {
            return new Gamma(this, inputVal, quantity, yearToDate).Compute();
        }
 
        public int delta()
        {
            return 1;
        }
    }
 
 
    public class Gamma
    {
        private Account_Refactored account;
        private int inputVal, quantity, yearToDate;
        private int importantValue1, importantValue2, importantValue3;
 
        public Gamma(Account_Refactored accountSource, int inputValArgs, int quantityArgs, int yearToDateArgs)
        {
            account = accountSource;
            inputVal = inputValArgs;
            quantity = quantityArgs;
            yearToDate = yearToDateArgs;
        }
 
        public int Compute()
        {
            int importantValue1 = (inputVal * quantity) + account.delta();
            int importantValue2 = (inputVal * yearToDate) + 100;
            if ((yearToDate - importantValue1) > 100)
                importantValue2 -= 20;
            int importantValue3 = importantValue2 * 7;
            // and so on.
            return importantValue3 - 2 * importantValue1;
        }
    }
}

namespace Refactoring_TestHarness
{
    using System;
    using RefactoryExampleLibrary.ReplaceMethodwithMethodObject;
 
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Replace method with method object");
            ReplaceMethodwithMethodObject replaceMethodwithMethodObject = new ReplaceMethodwithMethodObject();
            Console.WriteLine("{0}", replaceMethodwithMethodObject.ComputeGamma());
            Console.WriteLine("{0}", replaceMethodwithMethodObject.ComputeGammaRefactored());
        }
    }
}

The output of the above program is as below,

Replace method with method object
703
703
Press any key to continue . . .


Split temporary variable - Refactoring
I think this is  pretty good method to make code more understandable for other coder. For example in here, following code is less understandable 
long temp = Quantity * Price * 2;
temp = Quantity * temp; 


compare to  
long area = Quantity * Price * 2;
long length = Quantity * area;

I used following code to implement the concept,

namespace RefactoryExampleLibrary.SplitTemporaryVariable
{
    public class SplitTemporaryVariable
    {
        private long quantity = 10;
        private long price = 11;
 
        public SplitTemporaryVariable() { }
        /*
         * Before implementing the Split Temporary variables.
         */
        public long SplitTemporaryVariableBefore() {
            long temp = Quantity * Price * 2;
            temp = Quantity * temp; 
            return temp;
        }
        /*
         * After implementing the Split Temporary variables.
         */
        public long SplitTemporaryVariableAfter()
        {
            long area = Quantity * Price * 2;
            long length = Quantity * area;
            return length;
        }
 
        public long Quantity
        {
            get { return quantity; }
            set { quantity = value; }
        }
        public long Price
        {
            get { return price; }
            set { price = value; }
        }
    }
}


and following code to test the SplitTemporaryVariable,
namespace Refactoring_TestHarness
{
    using RefactoryExampleLibrary.SplitTemporaryVariable;
    using System;
 
    class Program
    {
        static void Main(string[] args)
        {
            SplitTemporaryVariable splitTemporaryVariable = new SplitTemporaryVariable();
            Console.WriteLine("Split Temporary Variable");
            Console.WriteLine("{0}",splitTemporaryVariable.SplitTemporaryVariableBefore());
            Console.WriteLine("{0}", splitTemporaryVariable.SplitTemporaryVariableAfter());
        }
    }
}


The output of the above code is as below,

Split Temporary Variable
110
110
Press any key to continue . . .



I will discuss about some other refactoring methods on part 2 of this article.

Happy to learn from others.

Thanks
mohammad

 Few C# and Application Design books from Amazon,

No comments:

Post a Comment