Sunday, October 03, 2010

Refactoring : Extract Method Refactoring Technique

Background

 

Few weeks back I was giving a presentation to my team on some of the best practices and  guidelines on design and Architecture of software systems. One of the topic for discussion was related to Refactoring. One of the team member requested me to show hands on refactoring techniques. I have read martin Fowlers book called Refactoring – Improving the Design of Existing Code and also gone through the online catalog that is maintained by him. I also referred the refactoring catalog available at industriallogic.com.

Refactoring is a simple technique which changes the internal behavior of code structure without affecting its functionality. One of the important prerequisite for refactoring is a good set of unit tests. This is my personal opinion based on my experiences with TDD. This doesn’t mean that you cannot apply refactoring techniques to legacy code which might not have been unit tested. If you have a set of unit tests it only helps to prove that the functionality is working the same way before refactoring the piece of code.

In this post I would like to demonstrate one of the most commonly used refactoring which is extracting a method. This method of refactoring is used when code is duplicated across two methods and it can be centralized in a single method. We can parameterize some of the inputs to the method if there is any need to do so.

Example without refactoring

Lets take a small example where we want to start a new process programmatically. Lets assume we want to start Notepad.exe and Microsoft Word exe using C# code. Here is how I would do it.

    public class ClassWithoutRefactoring
    {
        public void StartNotepadProcess()
        {
            Process notepadProcess = new Process
                {
                    StartInfo =
                        {
                            FileName = "Notepad.exe",
                            WindowStyle = ProcessWindowStyle.Maximized,
                        }
                };

            notepadProcess.Start();
        }

        public void StartMicrosoftWordProcess()
        {
            Process winwordProcess = new Process
            {
                StartInfo =
                {
                    FileName = "Winword.exe",
                    WindowStyle = ProcessWindowStyle.Maximized,

                }
            };

            winwordProcess.Start();
        }
    }


I have named the class as ClassWithoutRefactoring and defined two methods on it




  • StartNotepadProcess


  • StartMicrosoftWordProcess



I am using the Process class from System.Diagnostics namespace to create these processes. I have made use of the object initializer syntax to set the required properties for StartInfo property of process class. I would like to start the processes in Maximized mode which I have set using appropriate property. Finally I call the Start method to actually invoke the process.



To invoke these methods I create an instance of this class in the Main method and invoke the two methods as



            ClassWithoutRefactoring classWithoutRefactoring = new ClassWithoutRefactoring();

            classWithoutRefactoring.StartNotepadProcess();

            classWithoutRefactoring.StartMicrosoftWordProcess();


If we build and run the solution now we should see both the Notepad window as well as a Microsoft Word window open up in maximized mode. I am assuming that you have any one of the version of Microsoft Word installed on your machine. If not you can replace the Winword.exe with any exe name which you can directly invoke from run prompt like cmd.exe.



If you notice carefully, the two methods are exactly the same except for the name of the executable file that they are opening. Now assume that I want to start the two processes in normal window rather than maximized window.



For such a small method definition its not a big problem to make changes to any of these methods .Seldom in real life applications you’ll find methods which are very small and are used in only one or two places. It will be a problem to go and change in each and every place where similar code is repeated.



To avoid such situation we can restructure the implementation such that with minimal changes we can adapt to the changes.



Example with Extract Method Refactoring Technique



    public class ClassWithRefactoring
    {
        public void StartNotepadProcess()
        {
            StartProcess("Notepad.exe");
        }

        public void StartMicrosoftWordProcess()
        {
            StartProcess("Winword.exe");
        }

        private void StartProcess(string processName)
        {
            Process notepadProcess = new Process
                {
                    StartInfo =
                        {
                            FileName = processName,
                            WindowStyle = ProcessWindowStyle.Normal,
                            CreateNoWindow = true
                        }
                };

            notepadProcess.Start();
        }
    }


I could have modified the same methods but for the same of clarity I created a class called ClassWithRefactoring and implemented the methods as shown above. As can be seen I did not change the signatures of the public methods. They remain the same. But I extracted the common piece of code into a private method. I have also added the process name as the parameter to this method. Now I can make the change to open the process in Normal window. I need to make this change in only one place and it gets reflected in both the places.



This was a very simple example. You can also use extract method if a single method is doing too much of work. We can split a big method definition into smaller logical chunks to improve readability and maintainability of the code.



In fact the above method can be made even more easier by extracting some of the input parameters that we are passing to the StartInfo object. I would leave it to the readers to implement it themselves.



 



Conclusion



Refactoring helps us to improve the quality of code. It makes the code more readable, more understandable and most importantly more maintainable. If you have the tooling support it becomes even more easier. I have used the refactoring capabilities of both Visual Studio as well as Resharper. I prefer Resharper because it offers far more features than Visual Studio. May be that’s another post in itself for some other time.



I have uploaded the complete source code to dropbox which you can download and experiment ExtractMethodRefactoring.zip



Unit next time Happy Programming :)

3 comments: