Tuesday, 4 December 2012

Refactoring a Overview

           Are you working in Production support or maintenance or Enhancement project, and you are trying to make a difference technically where you have very less to prove your technical expertise, then you clicked on the right article about the Book, Refactoring by Martin fowler. Check out the book @ Refactoring by Martin fowler

Any idiot can write code a computer can understand, it takes care to write code another human can understand.


What does the quote means to us in this context, refactoring helps us to rewrite the code in human readable form.

Before beginning the actual process of refactoring let us see

What is the refactoring?

Refactoring changes the internal structure of the code WITHOUT affecting your code’s behavior.

List of questions you should  have answers before learning refactoring techniques.

Refactoring?


            A change made to the internal structure of the software to make it easier to understand and cheaper to modify without changing its observable behavior.


Why you should refactor ?


·        Refactoring improves the design of software
·        It makes software easier to understand
·        It helps you to find bugs
·        It helps you program faster


When you should Refactor ?


·        Use the Rule of Three, if you are changing the same area of code three times due to change in the requirement then the code should be refactored
·        Refactor when you add new function
·        Refactor when you fix a bug
·        Refactor when you do a code Review

When you shouldn’t Refactor your Code ?


·        When you should rewrite from scratch
·        When you are close to deadline


 When to use Refactoring (Bad Smells in Code)


Duplicated code


            When you find lots of duplicated code where there was lots of copy paste

Long Method


            When the method is very large, it should be refactored. Maximum allowable size is 25 lines per function. Shortcuts being replace the comments in the function of long method with function and give a proper name

Feature Envy


            If the class is very large and there are lots of methods available in the class and the method that seems to be more interested in a class than the one it is actually in.

Short Gun Surgery

           
            When every time you need to make a kind of change, you have to make a lot of little change to different classes.

Lazy Class

           
            A class that isn’t enough to pay its way for itself, then should be eliminated

Inappropriate Intimacy


            Sometimes classes become far too intimate and spend too much time delving with each other. Over intimate classes should be broken down

Request Bequest


            Subclasses get to inherit the methods and data of their parents. If they don’t want or need what they are inheriting then we should check the hierarchy of sub classing.

Steps to Refactoring


            Step 1: Write automated test cases for the method or classes, you are going to refactor
           
            Step 2: Separate large chunks of code into smaller functions. Run automated test cases; check the separation did not break the code

            Step 3: Rename any variable to remove clarity. Run automated test to check the functionality is not broken

            Step 4: Move any method or newly formed method to other classes, if they are not using any data in the class they belong too. Run automated test to check the functionality is not broken.

            Step 5: If possible apply patterns using principles. Run automated test to check the functionality is not broken

In the above steps always, whenever there is change of code for refactoring, make sure you run the automated test.
           

Technique #1: Extract Method


            Turn the fragment into a method whose name explains the purpose of the method

Motivation:


            If the method is very long then we apply this extract method.  If you find method's length more than 25 lines then this refactoring can be applied. But 25 lines may not be fixed criteria, it depends on the functionality too. If you find any functionality that can be grouped, then it can be moved to new method with proper naming which reflects the functionality from long method.

Look in below code PrintOwning method, where there is printing details of name and amount of the product. They are set of functionality that can be moved to separate method, so if we need to add any more print details about the product we can add it to the new method created


       
        public void PrintOwning(double amount)
        {
            PrintBanner();

            //print Details
            Console.WriteLine("Name" + _name);
            Console.WriteLine("Amount" + amount);
        }




Below code describes how the print Details is extracted as method and their functionality is defined as per the grouping the logical area and placing into a new method.

       
        public void PrintOwning(double amount)
        {
            PrintBanner();
            PrintDetails(amount);
         
        }

       
        public void PrintDetails(double amount)
        {

            Console.WriteLine("Name" + _name);
            Console.WriteLine("Amount" + amount);
        }

























Technique #2: Extract Class


            Create a new class and move the relevant fields and methods from old class into new class

Motivation

           
            A good sign is that subset of the data and subset of the methods seems to be together. Other signs are subset of data that usually changed together or are particularly dependant on each other.


            This refactoring method is based on Single responsibility principle of the class, Below Person class not only has details of a person, but it also has logic to manipulate phone number for every person. Currently the person class does more than one thing, best way is to move the phone number logic outside the Person Class


class Person
    {

        public string _name { get; set; }
        public string _OfficeAreaCode { get; set; }
        public string _officeNo { get; set; }

        public string GetName()
        {
            return _name;
        }


        public string GetTelephoneNo()
        {
            return GetOfficeAreaCode() + GetOfficeNo();
        }
        public string GetOfficeAreaCode()
        {
            return _OfficeAreaCode;
        }
        public string GetOfficeNo()
        {
            return _officeNo;
        }

    }





Below Person class is refactored to adhere to single responsibility principle. Manipulation of phone number is extracted out of person class and new class named Telephone is created which will take care of concatening proper telephone number. In future, if there is too much of logic needed for creating telephone number, then new class TelephoneNo can handle the change with less testing effort.


class PersonRefactored
    {
        public string _name { get; set; }
        public TelephoneNo telephoneNo { get; set; }
       
       
        public string GetName()
        {
            return _name;
        }

        public string GetTelephoneNo()
        {
            return telephoneNo.GetTelephoneNo();
        }
       
    }




    public class TelephoneNo
    {
        public string _OfficeAreaCode { get; set; }
        public string _officeNo { get; set; }

        public string GetTelephoneNo()
        {
            return _OfficeAreaCode + _officeNo;
        }

    }







































Technique #3: Replace Parameter with Explicit Methods


            You have a method that runs different code depending on the values of an enumerated parameter.

            Create a separate method for each value of the parameter.

            Below class sets height and width value based upon the name parameter, this turns out to be unhealthy coding style, because we also need to take care of sanitary check for the name variable

class Textbox
    {
        public int _height { get; set; }
        public int _width { get; set; }

        void SetValue(string name, int value)
        {
            if(name.Equals("height"))
                _height = value;
            if (name.Equals("width "))
                _width = value;
        }

    }




Below two explicit seperate method are created, named SetHeight and SetWidth which makes the Textbox refactored


    class TextboxRefactored
    {
        public int _height { get; set; }
        public int _width { get; set; }

        void SetHeight(int value)
        {
            _height = value;

        }
        void SetWidth(int value)
        {
            _width = value;
        }
    }




Technique #4: Replace Conditional with Polymorphism



            You have a condition that chooses the different behavior depending on the type of object.
            Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract.

Motivation

           
            In some projects, you find switch statements that switch on type codes or if then else statements that switch on type string.

There may be many scenarios where we need to calculate or identify the logic for business rules based on parameters. 
During initial stage of the project, in the code, if clause for branching the code with respect to parameters, may be one or two, once the user understand their own requirement, then starts the nightmare, if clause grows exponentially, because users start coming up with new scenario and use cases.

Look at the Employee class, the method GetSalary expains all. Employee salary is identified using   _type variable. Salary is calculated based on the employee type. Any change in the salary calculation should result in testing all the salary definition of the different employee types.

class Employee
    {
        private int _type;
        public static  int ENGINEER = 0;
        public static int SALESMAN = 1;
        public static int MANAGER = 2;

        private Employee(int type)
        {
            this._type = type;
        }
        public static Employee Create(int type)
        {
            return new Employee(type);
        }

        public double GetSalary()
        {
            double sal = 0;
            if (this._type == ENGINEER)
            {
                sal = 90000;
            }
            else if (this._type == SALESMAN)
            {
                sal = 50000;
            }
            else if (this._type == MANAGER)
            {
                sal = 70000;
            }
            return sal;
        }
    }







































    Below the employee class is refactored, where the employee has factory method which takes care of creating new employee type. Each and every type is now an employee class. Manager, Engineer, salesperson now represents a new entity and any logic or business rules can be added to the respective classes.

class EmployeeRefactored
    {
        public  const int ENGINEER = 0;
        public  const int SALESMAN = 1;
        public const int MANAGER = 2;

        public static EmployeeRefactored Create(int type)
        {
            switch (type)
            {
                case ENGINEER:
                    return new Engineer();
                case SALESMAN:
                    return new Manager();
                case MANAGER:
                    return new SalesPerson();
            }
        }

    }

    class Engineer:EmployeeRefactored
    {
        public double GetSalary()
        {

            return 90000;
        }
    }
    class Manager:EmployeeRefactored
    {
        public double GetSalary()
        {

            return 70000;
        }
    }
    class SalesPerson:EmployeeRefactored
    {
        public double GetSalary()
        {

            return 50000;
        }
    }



















































 

Technique #5: Replace Inheritance with Delegation


            A subclass uses only part of a super class interface or does not want to inherit data

Motivation

            Inheritance is wonderful things, we tend to inherit classes to use their methods but we might need only one or two methods, so that's the case then delegation for rescue


            Delegation is most important concept in OOPS. Delegation  reduces the coupling between two classes, meaning a change in one class does not affect the behaviour of the other class. So where ever we find the possibility of delegating certain functionality, go for it.

            Create a field for the super class; adjust methods to delegate to the super class, and removing the sub classing. In below example, MyStack Class inherits Vector so it can use the InsertAtElement() and RemoveAtElement() of vector class, it means if there is any change in superclass, it might also affect the subclasses too

class MyStack : Vector
    {
        public void Push(object element) {
            InsertAtElement(element, 0);
        }
        public object Pop() {
            object result = FirstElement();
            RemoveAtElement(0);
            return result;
        }

       
    }

    public class Vector
    {

        public void InsertAtElement(object element, int position)
        { }
        public void RemoveAtElement(int position)
        { }
        public object FirstElement()
        {
            throw new NotImplementedException();
        }

    }

           































After refactoring using delegation method, the vector is added as class variable the InsertAtElement () and RemoveAtElement() are delegated to vector. This is also called composition
  
class MyStackRefactored
    {
        private Vector _vector;

        public void Push(object element)
        {
            _vector.InsertAtElement(element, 0);
        }
        public object Pop()
        {
            object result = _vector.FirstElement();
            _vector.RemoveAtElement(0);
            return result;
        }
    }


           
        
















   
Now the above code uses delegation for functionality which was previously inherited.


     All the above refactoring methods are only a tip of an iceberg, I have carefully taken simple but effective refactoring techniques to get better understanding of the topic. First understand what is refactoring, write the unit test cases for the method or class you are going to refactor, then make sure each and any change in coding for refactoring should be tested by running the unit test case. Try applying the refactoring techniques in your fore coming projects or any module and learn more, because applying learned concept is a part of speed reading too. Talk to you soon.

Check out the book @ Refactoring by Martin fowler

No comments:

Post a Comment

Build Bot using LUIS