Skip to content

Refactoring – an emerging software development activity

August 5, 2009
tags:

Refactoring is a powerful technique for improving existing software. Having source code that is understandable helps ensure a system is maintainable and extensible. This paper describes the refactoring process in general and some of the benefits of using automated tools to reliably enhance code quality by safely performing refactoring tasks.

Overview

When a system’s source code is easily understandable, the system is more maintable, leading to reduced costs and allowing precious development resources to be used elsewhere. At the same time, if the code is well structured, new requirements can be introduced more efficiently and with less problems. These two development tasks, maintenance and enhancement, often conflict since new features, especially those that do not fit cleanly within the original design, result in an increased maintenance effort. The refactoring process aims to reduce this conflict, by aiding non destructive changes to the structure of the source code, in order to increase code clarity and maintainability.

However many developers and managers are hesitant to use refactoring. The most obvious reasons for this is the amount of effort required for even a minor change, and a fear of introducing bugs. Both of these problems can be solved by using an automated refactoring tool

Refactoring is a powerful technique for improving existing software. Having source code that is understandable helps a system to be maintainable and extensible. This paper describes the refactoring process in general and some of the benefits of using automated tools to reliably, enhance code quality by safely performing refactoring tasks.

Who needs refactoring?

Software starts on a small scale, and most of it well-designed. Over time, software size and complexity increases, with that bugs creep in, and thus code reliability decreases. Software developers, particularly when they are not the original authors, are finding it increasingly difficult to maintain the code, and even harder to extend. The code base, which in any software company should be a valuable asset, at some point may become a liability.

What is needed to prevent the software from ageing prematurely?
Strategically, attention of management and software developers is the most important factor. On the practical side, application of sound development methods will slow this ageing down. However refactoring can reverse this ageing when applied properly, preferably with good software tools that aid in the detection, analysis, and characterisation of the problems, and ultimately allow fixing them.

Well trained software developers who are intimately familiar with their code, are often acutely aware of the lurking code ageing problems. However, most developers would be reluctant to make changes to the structure of the code, especially if the changes may take some time. If developers find it easy to apply refactoring operations to their code, they will show less resistance to such restructuring work.

What exactly is refactoring?

Refactoring simply means “improving the design of existing code without changing its observable behaviour”. Originally conceived in the Smalltalk community, it has now become a mainstream development technique. While refactoring tools make it possible to apply refactorings very easily, its important that the developer understand what the refactoring does and why it will help in this situation eg allow reuse of a repetitive block of code.

Each refactoring is a simple process which makes one logical change to the structure of the code. When changing a lot of the code at one time it is possible that bugs were introduced. But when and where these bug were created is no longer reproducible. If, however, a change is implemented in small steps with tests running after each step, the bug likely to be layered in the test run immediately after introducing it into the system. Then the step could be examined or, after undoing the step, it could be split in even smaller steps which can be applied afterwards.

A refactoring operation proceeds in the following phases:

Action
Questions to ask, actions to take

Detect a problem
Is there a problem? What is the problem?

Characterise the problem
Why is it necessary to change something?
What are the benefits? Are there any risks?

Design a solution
What should be the “goal state” of the code?
Which code transformation(s) will move the code towards the desired state?

Modify the code
Steps that will carry out the code transformation(s) that leave the
code functioning the same way as it did before.

Example Refactorings:

Rename

A method, variable, class or other java item has a name that is misleading or confusing. This requires all references, and potentially file locations to be updated. The process of renaming a method may include renaming the method in subclasses as well as clients. On the other hand, renaming a package will also involve moving files and directories, and updating the source control system.

Move Class

A Class is in the wrong package, it should therefore be moved to another package where it fits better. All import statements or fully qualified names referring to the given class need to be updated. The file will also have to be moved and updated in the source control system.

Extract Method

A long method needs to be broken up to enhance readability and maintainability. A section of code with a single logical task (e.g. find a record by id) is replaced with an invocation to a new method. This new method is given suitable parameters, return type and exceptions. By giving the method a clear and descriptive name (findRecordById), the original method becomes simpler to understand as it will read like pseudocode. Extracting the method also allows the method to be reused in other places, not possible when it was tangled amongst the larger method. If the extracted section is well chosen, this method may be a natural place to change the behaviour of the class through subclassing, rather than a copy and paste of the existing method before making changes.

Extract Superclass

An existing class provides functionality that needs to be modified in some way. An abstract class is introduced as the parent of the current class, and then common behaviour is “pulled up” into this new parent. Clients of the existing class are changed to reference the new parent class, allowing alternative implementations (polymorphism). Any methods which are common to the concrete classes are “pulled up” with definitions, while those that will vary in subclasses are left abstract. As well as aiding in efficient code re-use, it also allows new subclasses to be created and used without changing the client classes.

Replace Conditional with Polymorphism

Methods in a class currently check some value (if or switch statement) in order to decide the right action to perform. One trivial example is a class that draws a shape, which is defined by a width and type (circle or square). The code quickly becomes confusing as the same if or switch statements are repeated throughout the class, i.e. in methods that calculate the area or perimeter of the shape. By using polymorphism, the shape specific behaviour can be offloaded to subclasses, simplifying the code. This has the added benefit of allowing other subclasses, e.g. rectangle or star, to be introduced without extensive code changes.

When should one consider refactoring?
Ideally, refactoring would be part of a continuing quality improvement process. In other words, refactoring would be flawlessly interconnect with other day-to-day activities of every software developer.
Refactoring may be useful, when a bug has surfaced and the problem needs to be fixed or the code needs to be extended. Refactoring at the same time as maintenance or adding new features, also makes management and developers more likely to allow it, since it will not require an extra phase of testing.

What are the benefits of refactoring?

Programs that are hard to read are hard to modify.
Programs that have duplicate logic are hard to modify
Programs that require additional behaviour that requires you to change running code are hard to modify.
Programs with complex conditional logic are hard to modify.

Why use an automated tool?

When doing refactoring the externally observable behaviour must be guaranteed to remain same. If refactorings are carried out manually, one needs to frequently rebuild the system and run tests. Manual refactoring is therefore really practical only, when the following conditions hold:

The system, of which the refactored code is a part, can be rebuilt quickly.
There are automated “regression” tests that can be frequently run.
This situation is not very common, meaning that the applications of refactoring is limited. This situation is becoming more common, particularly as more people use XP (Extreme Programming) development methods.

Another obstacle is that many of these refactoring are tedious. Potentially requiring hours of precious development time, as the change is made and then thoroughly checked. Not many programmers would enjoy the task of renaming a method in a large code base. A simple search and replace will potentially find extra results. So each replacement must be examined by the programmer. However there is no great intelligence to the operation, all that is wanted is to rename any use of a method on a given class or its subclasses. A refactoring tool therefore can save hours of work. Even more importantly give confidence that the correct changes were made.

The speed of automated tools has another benefit, which is shown in team development environments. A developer is much less likely to perform refactoring operations if the source code involved is under the responsibility of other developers as well. However by using an automated tool, the refactorings can be completed quickly so that other developers are not held up waiting to make there changes when the refactoring is completed, or worse making their changes on the old code at the same time as the refactoring operation. This ensures that even when the responsibility for a section of code is shared, developers will not reach a deadlock, where none of the developers make the required changes.

Integration with the developers chosen IDE also bring many benefits. Firstly having the tools at hand, means that developers can more easily refactor. They do not have to switch between development and refactoring modes, and can instead see it as part of their normal development cycle. Secondly IDE features such as Source Control Integration can reduce the effort in refactorings such as move class or rename package.

Conclusion

Refactoring is a well defined process that improves the quality of systems and allows developers to repair code that is becoming hard to maintain, without throwing away the existing source code and starting again. By careful application of refactorings the system’s behaviour will remain the same, but return to a well structured design. The use of automated refactoring tools, makes it more likely that the developer will perform the necessary refactorings, since the tools are much quicker and reduce the chance of introducing bugs.

Advertisements
No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: