Refactoring is the process of restructuring code, while not changing its original functionality. The goal of refactoring is to improve internal code by making many small changes without altering the code’s external behavior.
Computer programmers and software developers refactor code to improve the design, structure and implementation of software. Refactoring improves code readability and reduces complexities. Refactoring can also help software developers find bugs or vulnerabilities hidden in their software.
The refactoring process features many small changes to a program’s source code. One approach to refactoring, for example, is to improve the structure of source code at one point and then extend the same changes systematically to all applicable references throughout the program. The thought process is that all the small, behavior-preserving changes to a body of code have a cumulative effect. These changes preserve the software’s original behavior and do not modify its behavior.
Martin Fowler, considered the father of refactoring, consolidated many best practices from across the software development industry into a specific list of refactorings and described methods to implement them in his bookRefactoring: Improving the Design of Existing Code.
What is the purpose of refactoring?
Refactoring improves code by making it:
More efficient by addressing dependencies and complexities.
More maintainable or reusable by increasing efficiency and readability.
Cleaner so it is easier to read and understand.
Easier for software developers to find and fix bugs or vulnerabilities in the code.
Code modification is done without changing any functions of the program itself. Many basic editing environments support simple refactorings like renaming a function or variable across an entire code base.
This image shows the process of making several small code refactorings.
When should code be refactored?
Refactoring can be performed after a product has been deployed, before adding updates and new features to existing code, or as a part of day-to-day programming.
When the process is performed after deployment, it is normally done before developers move on to the next project. An organization may be able to refactor more code at this point in the software delivery lifecycle, where the developers have increased availability and more time to work on the source code changes needed.
A better time to perform refactoring, though, is before adding updates or new features to existing code. When performed at this point, refactoring makes it easier for developers to build onto the existing code because they are going back and simplifying the code, making it easier to read and understand.
When an organization has a strong grasp on the refactoring process, it can make it a regular process. Whenever a developer needs to add something to a code base, they can look at the existing code to see if it is structured in a way that would make the process of adding new code straightforward. If it is not, then the developer can refactor the existing code. Once the new code is added, the developer can refactor the same code again to make it clearer.
what is Clean code?
Clean code is code that has any extraneous (främmande) code removed from it. That is why refactoring your code is one of the best ways to make sure your codebase is clean and efficient. There are plenty of C# code refactoring tools on the market, including JetBrains’ ReSharper.8
What are the benefits of refactoring?
Refactoring can provide the following benefits:
Makes the code easier to understand and read because the goal is to simplify code and reduce complexities.
Improves maintainability and makes it easier to spot bugs or make further changes.
Encourages a more in-depth understanding of code. Developers have to think further about how their code will mix with code already in the code base.
Focus remains only on functionality. Not changing the code’s original functionality ensures the original project does not lose scope.
What are the challenges of refactoring?
Challenges do come with the process, however. Some of these include:
The process will take extra time if a development team is in a rush and refactoring is not planned for.
Without clear objectives, refactoring can lead to delays and extra work.
Refactoring cannot address software flaws by itself, as it is made to clean code and make it less complex.
Techniques to perform code refactoring
Organizations can use different refactoring techniques in different instances. Some examples include:
Red, green. This widely used refactoring method in Agile development involves three steps. First, the developers determine what needs to be developed; second, they get their project to pass testing; and third, they refactor that code to make improvements.
Inline. This technique focuses on simplifying code by eliminating unnecessary elements.
Moving features between objects. This technique creates new classes, while moving functionality between new and old data classes.
Extract. This technique breaks down code into smaller pieces and then moves those pieces to a different method. Fragmented code is replaced with a call to the new method.
Refactoring by abstraction. This technique reduces the amount of duplicate code. This is done when there is a large amount of code to be refactored.
Compose. This technique streamlines code to reduce duplications using multiple refactoring methods, including extraction and inline.
Code refactoring best practices
Best practices to follow for refactoring include:
Plan for refactoring. It may be difficult to make time for the time-consuming practice otherwise.
Refactor first. Developers should do this before adding updates or new features to existing code to reduce technical debt.
Refactor in small steps. This gives developers feedback early in the process so they can find possible bugs, as well as include business requests.
Set clear objectives. Developers should determine the project scope and goals early in the code refactoring process. This helps to avoid delays and extra work, as refactoring is meant to be a form of housekeeping, not an opportunity to changes functions or features.
Test often. This helps to ensure refactored changes do not introduce new bugs.
Automate wherever possible. Automation tools make refactoring easier and faster, thus, improving efficiency.
Fix software defects separately. Refactoring is not meant to address software flaws. Troubleshooting and debugging should be done separately.
Understand the code. Review the code to understand its processes, methods, objects, variables and other elements.
Refactor, patch and update regularly. Refactoring generates the most return on investment when it can address a significant issue without taking too much time and effort.
Focus on code deduplication. Duplication adds complexities to code, expanding the software’s footprint and wasting system resources.
Refactoring C# Code Using Visual Studio
Code Refactoring – An Overview
Refactoring is the process of improving the performance of the code after it has been developed by changing only the internal structure and not changing the external functionality of the code. Refactoring improves the non-functional attributes of the application such as readability, maintainability, and extensibility of the code. It also reduces the complexity and can also help software developers to find and fix any hidden bugs in the application by removing unnecessary complexity and by simplifying the program logic.
On the other hand, if the refactoring is not done well, it can lead to the introduction of new bugs in the system or break the existing functionalities. When we are continuously refactoring the code, it becomes more and more easy to work with. However, the need for urgent maintenance activities, need to avoid downtime and the pressure to deliver features to the customer quickly prioritizes the time-to-market factor over the code quality.
Insights on Code Smell
Agile Programmers use the term “Code Smell” to indicate that characteristic of the source code which indicates a deeper problem. The term was coined by Kent Beck in the late 1990s and refers to certain structures in the code that indicate a violation of fundamental design principles that negatively impact design quality. Code smells are not bugs, and they do not prevent the program from functioning. Instead, they indicate some other weaknesses in design that may slow down the development or increase the risk of bugs or failures in the future. Bad code smells can be an indicator of factors that contribute to “technical debt” in the case of agile sprints.
Some common code smells are listed below.
Examples of Application level smells:
a) Duplicated Code: An identical or similar code which is present in more than one location
b) Contrived Complexity: Usage of highly complicated design patterns where simple design would have been sufficient
c) Shotgun Surgery: A lone change needs to be applied across multiple classes at the same time
Examples of Class level smells:
a) Large Class: A class that has become too large
b) Feature Envy: A class which is using the methods of another class in excess
c) Inappropriate Intimacy: a class which has dependencies on the implementation of another class
d) Refused Bequest: A class that overrides a method of a base class in such a way that the contract of the base class is not honored by the derived class
e) Lazy Class / Freeloader: A class that does too little
Examples of Method level smells:
a) Too many parameters: A very long list of parameters is difficult to read. The method call and testing of the function become complicated. It may indicate that the purpose of the function is not well conceived, and the code should be refactored in a way that responsibility is assigned clearly
b) Long method: A method, function, or procedure that has grown too large
c) Excessively long identifiers: The use of naming conventions to provide disambiguation. This should be implicit in the software architecture
d) Excessively short identifiers: The name of a variable should reflect its actual function unless it is self-explanatory
e) Excessive return of data: a function or method that returns more than what each of its callers needs
f) Extreme long lines of code: A line of code which is very long, difficult to understand, to debug, to refactor or to check for reusability options
Visual Studio Refactoring Menu:
Visual Studio Professional Edition contains the Refactor option under the main Edit menu of the IDE. This menu item will get enabled only when a C# file is kept open in the IDE.
In the Visual Studio 2017 Edition (it is different in different version), we have the following options under the Refactor menu for refactoring the C# code:
Extract Method Refactoring
Rename Refactoring
Encapsulate Field Refactoring
Extract Interface Refactoring
Remove Parameters Refactoring
Reorder Parameters Refactoring
Refactor menu in Visual Studio
Extract Method Refactoring:
Extract Method is one of the C# refactoring techniques which provides a way to create a new method from a code fragment in an existing member.
Using the Extract Method, we can create a new method by extracting a selection of code from inside the code block of an existing member. The new, extracted method contains the selected code only and the selected code in the existing member is replaced with a call to the new method. Turning a fragment of code into its own method lets us to quickly and accurately reorganize code for better reuse and readability.
1. Select the code fragment you want to extract.
2. On the Refactor menu, click Extract Method.
3. The Extract Method dialog box appears.
Alternatively, we can also type the keyboard shortcut CTRL+R, M to display the Extract
Method dialog box or we can also right-click the selected code, point to Refactor,
and then click Extract Method to display the Extract Method dialog box.
4. Enter a name for the new method in the New Method Name box.
5. A preview of the new method’s signature is shown under the Preview Method Signature.
6. Click OK.
2. Rename Refactoring:
Rename is another C# refactoring technique that provides a way to rename the code identifiers such as fields, local variables, methods, namespaces, properties, and types. Rename can be used to change the names in the comments and in strings also and also to change the declarations and calls of an identifier.
How to Invoke Rename Refactoring:
a. Code Editor: In the Code Editor, rename refactoring is available when you position the cursor on certain types of code symbols. When the cursor is in this position, we can invoke the Rename command by typing the keyboard shortcut (CTRL + R, CTRL + R), or by selecting the Rename command from the shortcut menu or Refactor menu.
b. Class View: When we select an identifier in the Class View, rename refactoring is enabled in the shortcut menu and Refactor menu.
c. Object Browser: When we select an identifier in the Object Browser, the rename refactoring is available only from the Refactor menu.
d. Property Grid: Changing the name of a control will initiate a renaming operation for that control. The Rename dialog box will not appear.
e. Solution Explorer: Rename command is available on the shortcut menu.
Rename Operations:
1. Field: Changes the declaration and usages of the field to the new name.
2. Local Variable: Changes the declaration and usages of the variable to the new name.
3. Method: Changes the name of the method and all references to that method to the new name including static and instance methods.
4. Namespace: Changes the name of the namespace to the new name in the declaration, all using statements, and fully qualified names.
5. Property: Changes the declaration and usages of the property to the new name.
6. Type: Changes all declarations and all usages of the type to the new name, including constructors and destructors.
3. Encapsulate Field Refactoring:
The Encapsulate Field is a refactoring operation which enables you to quickly create a property from an existing field, and then seamlessly update your code with references to the new property.When any field is declared as public, other objects have direct access to that field and can modify it, undetected by the object which owns the field. By using properties to encapsulate and protect the field, you can disallow any direct access to the fields.
To create the new property, the Encapsulate Field operation first changes the access modifier for the field that we want to encapsulate to private. Next, it generates the Get and Set accessors for that field. In some cases, only a Get accessor is generated, such as when a field is declared as read -only. The refactoring engine now updates the code with references to the new property in the areas mentioned in the Update References section of the Encapsulate Field dialog box.
How to Use:
1. In the Code Editor, place the cursor in the declaration, on the name of the field that you want to encapsulate.
2. On the Refactor menu, click Encapsulate Field.
3. The Encapsulate Field dialog box appears. We can also type the keyboard shortcut CTRL+R, E to display the Encapsulate Field dialog box. We can also right-click the cursor, point to Refactor, and then click Encapsulate Field to display the Encapsulate Field dialog box.
4. Specify settings and Click the OK button.
5. If you selected the Preview reference changes option, then the Preview Reference Changes window opens. Click the Apply button.
4. Extract Interface Refactoring:
Extract Interface is a refactoring operation that provides an easy way to create a new interface with members that originate from an existing class, struct, or interface. When several clients use the same subset of members from a class, struct, or interface, or when multiple classes, structs, or interfaces have a subset of members in common, it can be useful to place the subset of members in a separate interface. The Extract Interface generates an interface in a new file and places the cursor at the beginning of the new file. We can specify which members to extract into the new interface, the name of the new interface, and the name of the generated file using the Extract Interface dialog.
How to Use:
1. With the cursor positioned on the method, click Extract Interface on the Refactor menu. The Extract Interface dialog box appears.
2. We can also type the keyboard shortcut CTRL+R, I to display the Extract Interface dialog box.
3. Or we can also right-click the mouse, point to Refactor, and then click Extract Interface to display the Extract Interface dialog box.
4. Click Select All.
5. Click OK.
5. Remove Parameters Refactoring:
Remove Parameters is a refactoring technique which provides a way to remove parameters from methods, indexers, or delegates. Remove Parameters changes the declaration of the method and at the locations where the member is called, the parameter is removed to reflect the new declaration.
We perform the Remove Parameters operation by first positioning the cursor on a method, indexer, or delegate. While the cursor is in position, to invoke the Remove Parameters operation, click the Refactor menu, press the keyboard shortcut, or select the command from the shortcut menu.
How to Use:
1. Place the cursor on method A, either in the method declaration or the method call.
2. From the Refactor menu, select Remove Parameters to display the Remove Parameters dialog box. We can also type the keyboard shortcut CTRL+R, V to display the Remove Parameters dialog box. We can also right-click the cursor, point to Refactor, and then click Remove Parameters to display the Remove Parameters dialog box.
3. Using the Parameters field, position the cursor on the parameter and then click Remove.
4. Click OK.
5. In the Preview Changes — Remove Parameters dialog box, click Apply.
6. Reorder Parameters Refactoring:
Reorder Parameters is one of the C# refactoring techniques that provides a way to change the order of the parameters for the methods, indexers, and delegates. Reorder Parameters modifies the declaration first and at any locations where the member is being called, the parameters are rearranged as per the new order.
In order to perform the Reorder Parameters operation, place the cursor next to a method, indexer, or delegate. When the cursor is in position, invoke the Reorder Parameters operation by pressing the keyboard shortcut, or by clicking the command from the shortcut menu.
How to Use:
1. Place the cursor on the method, either in the method declaration or the method call.
2. On the Refactor menu, click Reorder Parameters.
3. The Reorder Parameters dialog box appears.
4. In the Reorder Parameters dialog box, select the parameter in the Parameters list, and then click the down button. Alternatively, you can drag the parameter after the destination parameter in the Parameters list.
5. In the Reorder Parameters dialog box, click OK.
6. If the Preview reference changes option is selected in the Reorder Parameters dialog box, the Preview Changes – Reorder Parameters dialog box will be displayed. We can see the preview of the changes in the parameter list for the Method in both the signature as well as the method call.
7. If the Preview Changes – Reorder Parameters dialog box appears, click Apply.
Conclusion
Thus, code refactoring continues to be an important practice that must be done frequently and concurrently in order to reduce the complexity of the code. The benefits of refactoring are multi-dimensional, therefore the developers and managers would be benefitted from the automated tool support for monitoring the impacts of refactoring.