Overview:
This concept explains ways of improving the design of existing code in a way that does not alter its external behavior.
Main Description:
Refactoring is a disciplined way to restructure code when small changes are made to the code to improve its design. An important aspect of a refactoring is that it improves the design while not changing the behavior of the design; a refactoring neither adds nor removes functionality.
Refactoring enables you to evolve the code slowly over time, to take an iterative and incremental approach to implementation.
These are the types of refactoring:
- Code refactoring. Often referred to simply as refactoring, this is the refactoring of programming source code. Examples of code refactorings include Rename Method, Encapsulate Field, Extract Class, Introduce Assertion, and Pushdown Method.
- Database refactoring. A database refactoring is a simple change to a database schema that improves its design while retaining both its behavioral and informational semantics. Examples of database refactorings include Rename Column, Split Table, Move Method to Database, Replace LOB with Table, Introduce Column Constraint, and Use Official Data Source.
- User interface (UI) refactoring. A UI refactoring is a simple change to the UI which retains its semantics. Examples of UI refactorings include Align Entry Fields, Apply Common Button Size, Apply Common Font, Indicate Format, Reword in Active Voice, and Increase Color Contrast.
Martin Fowler identifies four key reasons to refactor:
- Refactoring improves the design of software
- Refactoring makes software easier to understand
- Refactoring helps you find bugs
- Refactoring helps you program faster
Refactoring can improve the design of existing code, but it does not take the place of considering the design before writing code. Refactoring instead changes the role of up-front design, allowing the strictly design work to be more abstract. Small-scale, very tactical decisions can be made during the implementation of the solution with confidence that refactoring will ensure a quality implementation at that level. The designing of the solution before implementation will be more lightweight and focused on broad factors that will drive the implementation.
There is an additional benefit of refactoring: it changes the way a developer thinks about the implementation when not refactoring. The basic task of implementing a solution becomes solely about getting the solution to pass its developer tests in the simplest way possible. Then the design of that solution can be examined and refactored separately. Even if these two things -- implementation of the solution and then improvement -- are just a minute apart, it can be freeing for a developer to single-mindedly create code that causes a test to pass, and then separately single-mindedly improve that code.
Guideline:
Refactoring involves improving the design of existing code without changing the system's behavior. It is explicitly not about adding or changing behavior, but about improving the design of existing behavior.
A full set of developer tests is required before refactoring can be safely applied. It is critical that the system behavior be in a known, verifiably correct state before modifying the design so that you can improve the design without fear that the modified implementation will break something. Refactoring is a safe transformation to improve code, but it is safe only if there are tests that can verify that the system still works as intended.
Refactoring is initiated when an area that needs improvement is identified in the system by examining either the code or some other representation of the design. The issues identified are sometimes called "smells."
Here are several smells to look for that might lead to refactoring:
Duplicated code: Duplicated code makes the system harder to understand and harder to maintain.
Large design element or method: Large design elements or methods diminish the ability of people to understand the design, reduce the potential for reuse, and make developer testing more difficult.
Poorly named element: Whether the element be a variable, function, class, or component, its name should connote what it is so that the code can be maintained.
Tight coupling: Each design element should work with minimal concern for the internal aspects of other design elements. Otherwise, changes to one element can have undesirable effects in other elements.
As you can see from this list, refactoring can improve the "internals" of a design element, the interface of the element, or the relationships and collaboration between elements. Also, many of the smells are characterized as making the software more difficult to understand; whereas refactoring is about making the system simpler.
After an issue is identified, a refactoring method can be selected that will improve the situation. There are catalogs of refactoring methods available that are change patterns that will fix common problems while retaining the behavior of the system.
These are examples of refactoring methods:
Extract Method: Pull out the duplicated code into its own single method or extract part of a large method into its own method.
Extract Class: Pull some cohesive part of a class into its own class to reduce the size of a design element that is too big.
Rename Method, Rename Class, or Rename Variable: Give a more meaningful name to an element to make it more understandable.
Extract Interface: Create a clean interface to an element to reduce coupling.
When refactoring has been applied, developer tests are run again to ensure that the system still behaves correctly. It is important that the system is working correctly after each small refactoring. Although many refactorings can be put together to drive broad change across the code base, the tests should run correctly between each refactoring applied. Refactoring must be applied as small behavior-preserving transformations.
As mentioned previously, refactoring requires full developer test coverage of the area under consideration. There are additional techniques that enable refactoring. Coding standards define a common style and make it easier to refactor consistently. An attitude of collective code ownership within the team is important. Each developer should feel that refactoring can be applied across the code base to improve the implementation.