Refactoring in C#
Refactoring in C#: is the process of restructuring and improving the internal structure of your code without altering its external behavior.
The goal is to enhance :
-
Efficiency
-
Code readability
-
Maintainability
When should we refactor the code?
These are some situations when we should refactor the code
-
Refactor when the code becomes hard to read or understand
-
When you see a code smell its time to refactor
-
Refactor when fixing bugs ( if you feel need of it)
-
Refactor the code when better performance is your concern. Although it should be kept in mind while developing first time.
-
Integrate refactoring when dealing with legacy code to modernize and align it with current coding standards and practices
Important : Make sure that your all test cases pass after refactoring the code
Common refactoring techniques
Here are some common refactoring techniques in C#:
1/ Create a new method to encapsulate a portion of existing code
If you have a lengthy code block performing a specific task within a method, you can extract that block into a separate method with a meaningful name.
Suppose we have this code :
public bool ValidateUserInput(string username, string password)
{
if (!string.IsNullOrEmpty(username) &&
!string.IsNullOrEmpty(password) &&
password.Length >= 8)
{
return true;
}
else
{
return false;
}
}
It can be replaced with this :
public bool ValidateUserInput(string username, string password)
{
if (IsUsernameValid(username) && IsPasswordValid(password))
{
return true;
}
else
{
return false;
}
}
2/ Improve code clarity by giving variables more descriptive and meaningful names
If a variable has a vague or misleading name, renaming it can improve code clarity
// Before refactoring
int x = CalculateTotal();
// After refactoring
int total = CalculateTotal();
3/ Replace hard-coded numerical values with named constants
If you have a constant value used in multiple places, define it as a named constant to enhance code readability and maintainability.
// Before refactoring
int result = someValue * 42;
// After refactoring
const int MULTIPLIER = 42;
int result = someValue * MULTIPLIER;
4/ Group related parameters into a single object to simplify method signatures
If a method has multiple parameters that logically belong together, create a parameter object to make the method more cohesive.
// Before refactoring
public void SavePerson(string name, string email, int age)
{
// Existing Code
}
...
// After refactoring
public void SavePerson(Person person)
{
// Existing Code
}
5/ Replace conditional statements with polymorphic behavior through inheritance and interfaces
If you have a series of conditional statements that determine the behavior of an object, consider using polymorphism for a more extensible and maintainable solution.
// Before refactoring
public double CalculateBonus(Employee employee)
{
if (employee is Manager)
{
return employee.Salary * 0.2;
}
else if (employee is Developer)
{
return employee.Salary * 0.1;
}
else
{
return 0;
}
}
// After refactoring
public abstract class Employee
{
public abstract double CalculateBonus();
}
public class Manager : Employee
{
public override double CalculateBonus()
{
return Salary * 0.2;
}
}
public class Developer : Employee
{
public override double CalculateBonus()
{
return Salary * 0.1;
}
}
6/ Replace a class hierarchy with composition to achieve greater flexibility and maintainability
Instead of using deep class hierarchies, favor composition to assemble behavior from smaller, more focused components.
// Before refactoring
public class Employee
{
// Common
}
public class Manager : Employee
{
// Specific
}
// After refactoring
public class Employee
{
// Common
}
public class Manager
{
private Employee _employee;
public Manager(Employee employee)
{
_employee = employee;
}
// Specific
}
7/ Define an interface based on the public methods of a class to promote loose coupling and flexibility
If multiple classes share similar behavior, extract an interface to enable polymorphism and interchangeability.
// Before refactoring
public class Logger
{
public void LogMessage(string message)
{
// existing code
}
}
// After refactoring
public interface ILogger
{
void LogMessage(string message);
}
public sealed class Logger : ILogger
{
public void LogMessage(string message)
{
// existing code
}
}
8/ Give appropriate variable names in lengthy expressions for better understanding
In this refactoring, we break down the complex expression into more manageable parts.
// Before refactoring
double totalCost = (quantity * unitPrice) + ((quantity * unitPrice) * taxRate) - ((quantity * unitPrice) * discountRate);
// After refactoring
double subtotal = quantity * unitPrice;
double taxAmount = subtotal * taxRate;
double discountAmount = subtotal * discountRate;
double totalCost = subtotal + taxAmount - discountAmount;
Happy Refactoring :)
Whenever you're ready, there are 3 ways I can help you:
- Subscribe to my youtube channel : For in-depth tutorials, coding tips, and industry insights.
- Promote yourself to 9,000+ subscribers : By sponsoring this newsletter
- Patreon community : Get access to all of my blogs and articles at one place