The 6 Types of Code Refactoring That Every Programmer Should Know

Refactoring is the process of changing the current codes of software to make it easier to understand and maintain without modifying its internal functionality.


Repeating code that does the same thing leads to poor design. By reducing these repeating codes to one, you make certain that aimed work is done only in one method. In this way, the readability of the code also increases.


It increases the development pace because it prevents system design delays. As you get rid consequences of poor design with the help of refactoring, it makes the current design better.


Reading and understanding codes become easier by refactoring. This leads to recognizing the bugs easily.


Changing current code or adding new features takes a longer time while making an effort to figure out the logic and scan for bugs. Because delays in development is gotten rid of with refactoring, the development takes shorter time rather than it is in the previous situation.


Refactoring makes it easier to figure out logic of the code. Then, you can add a feature rapidly and with an ease.


If you are required to fix a bug and codes are clear enough to find the bug, it is an appropriate time to refactor.


Code review enables to pass knowledge through a team. In this way, less experienced ones can learn more about structure of the system. It also leads to clear coding. Reviewer and person whose codes are reviewed can make a refactoring decision at time of review and do so.


Refactoring brings along a comprehensive design, enabling development easier. Because It leads to a simpler structure, it becomes easier to figure out over-all logic and takes less time to make development. And, this provides you with time to take care performance.

n Codes are separated to parts according to single responsibility. In this way, complexity is eliminated, leading to understand easily. And, it offers you additional time to adjust the codes for a better performance.


Here is some samples of Refactoring in different areas.


Extracting Method

If a method is long or its purpose is explained with comments, then that code is carried into a new method. Method extracting makes it possible to reuse method. If method name explains method body clearly, it doesn’t matter whether it is long.


Pattern(Each step is told with a number on the example below.)

Define a new method, and name it by what it does
Move the extracted code from the source method into the new target method
Analyze whether local-scope variables are affected by extraction. If a variable is affected, consider whether you can assign the result of the extracted code to the variable.
Send local-scope variables from extracted code into the target method as parameters
Change extracted code to call to the new method

bool ValidateTransactionBeforeSave(){
if(IsNullOrEmpty(document.getElementById(“CustomerNumber”))){ → (5)
alert(“CustomerNumberRequiredMessage”); → (3)
return false;
}
.
.
. → Assume that if block above is repeated for a transactions’ header fields. (2)

if(IsNullOrEmpty(document.getElementById(“PaymentMaturityStartDate”))){ → (5)
alert(“PaymentMaturityStartDateRequiredMessage”); → (3)
return false;
}
.
.
. → Assume that if block for PaymentMaturityStartDate is repeated for the transactions’ other payment details on defined dates. (2)

return true;
}


After extracting the method above;

bool ValidateTransactionBeforeSave(){
Result headerResult = ValidateTransactionHeader();
if(!headerResult.IsValid){
alert(headerResult.Message); → (3)
return false;
}

Result detailsResult = ValidateTransactionDetails();
if(!detailsResult.IsValid){
alert(detailsResult.Message); → (3)
return false;
}

return true;
}

Result ValidateTransactionHeader(){ → (1)
Result headerValidationResult = new Result();

if(IsNullOrEmpty(document.getElementById(“CustomerNumber”))){
headerValidationResult.Message = “CustomerNumberRequiredMessage”;
headerValidationResult.Result = false;
}
.
.
. → Here, validation is repeated for CustomerName, BranchCode, BranchName, DealerCode, DealerName, TranDate, MaturityDate, TransactionAmount, TransactionUnit.

return headerValidationResult;
}

Result ValidateTransactionDetails(){ → (1)
Result detailValidationResult = new Result();

if(IsNullOrEmpty(document.getElementById(“PaymentMaturityStartDate”))){
detailValidationResult.Message = “PaymentMaturityStartDateRequiredMessage”;
detailValidationResult.Result = false;
}

.
.
. → Here, validation is repeated for PaymentMaturityEndDate, PaymentSettlementDate, PaymentTransactionAmount, PaymentTransactionUnit.

return detailValidationResult;
}


Moving Method

It is time to move methods when classes are highly coupled and have too much responsibilities. If a method interacts with another object than the object it lives on, it is a leading criteria to change.


Pattern(3. 4. and 7. steps are told with a number on the example below.)

Scan other methods using method you consider to move
Look through for methods performing the same work with the method in the sub and super classes
Create method in target class
Move the code to the target class
Make it work in target class by providing with necessary pieces
Replace the source code by delegating to the new method
Replace references of old method with ones to the target method

class Transaction {
private TransactionType transactionType;
private double assetAmount;
private double assetUnit;
private double secondAssetAmount;
private double secondAssetUnit;
private double parity;

double CalculateTransactionAmount(){ → (4)
double transactionAmount = 0;

if(transactionType.IsType1()){
transactionAmount = (assetAmount * assetUnit * parity) + (secondAssetAmount * secondAssetUnit * parity);
}
else {
transactionAmount = assetAmount * assetUnit * parity;
}

return transactionAmount;
}
}


After extraction;

class TransactionType {

double CalculateTransactionAmount(Transaction transaction){ → (3)
double transactionAmount = 0;

if(IsType1()){
transactionAmount = transaction.getAssetAmount() *
transaction.getAssetUnit() *
transaction.getParity() +
transaction.getSecondAssetAmount() *
transaction.getSecondAssetUnit() *
transaction.getParity();
}
else {
transactionAmount = transaction.getAssetAmount() * transaction.getAssetUnit() * transaction.getParity();
}

return transactionAmount;
}
}

class Transaction {
private TransactionType transactionType;
private double assetAmount;
private double assetUnit;
private double secondAssetAmount;
private double secondAssetUnit;
private double parity;

public double getAssetAmount(){ return assetAmount; }
public double getAssetUnit(){ return assetUnit; }
public double getSecondAssetAmount(){ return secondAssetAmount; }
public double getSecondAssetUnit(){ return secondAssetUnit; }
public double getParity(){ return parity; }
public double getTransactionAmount(){
return transactionType.CalculateTransactionAmount(); → (6)
}
}


Extracting Class

If a class has many methods and a lot of data, you are required to take into account how to shorten it. A good clue is that a set of data and methods work together and are depend on each other.


Pattern

Separate the class by extracting variables and methods you thing they don’t belong the class
Define a new class
Reference to the new class from the old one
Move fields and methods you determined to the new class

class Transaction{
private DateTime tranDate;
private double transactionAmount;
private string transactionUnit;
private int customerNumber;
private string customerName;
private string customerType;
private string customerSwiftCode;

public DateTime getTranDate(){ return tranDate; }
public double getTransactionAmount(){ return transactionAmount; }
public string getTransactionUnit(){ return transactionUnit; }

public int getCustomerNumber(){ return customerNumber; } → (4)
public string getCustomerName(){ return customerName; } → (4)
public string getCustomerType(){ return customerType; } → (4)
public string getCustomerSwiftCode(){ return customerSwiftCode; } → (4)

.
.
.
}


After extraction;

class Customer{ → (2)
private int customerNumber;
private string customerName;
private string customerType;
private string customerSwiftCode;

public int getCustomerNumber(){ return customerNumber; }
public string getCustomerName(){ return customerName; }
public string getCustomerType(){ return customerType; }
public string getCustomerSwiftCode(){ return customerSwiftCode; }

.
.
.
}

class Transaction{
private DateTime tranDate;
private double transactionAmount;
private string transactionUnit;

private Customer customer = new Customer(); → (3)

private int getCustomerNumber { return customer.getCustomerNumber(); }
private string getCustomerName { return customer.getCustomerName(); }
private string getCustomerType { return customer.getCustomerType(); }
private string getCustomerSwiftCode { return customer.getCustomerSwiftCode(); }
}


Using Array instead of Object

Despite that arrays should keep only objects of the same type, they are used to strore different objects. It may be sometimes difficult to find out which element on the array represent what meaning. If you use an object instead of array, fields and methods carry purpose of information.


Pattern

Generate a class to keep information in the array
Generate a public property in the class for each item of the array
Assign each array element to related property on the class
After successful build, delete the array

object[] emailInfos = new object[6]; → (4)
emailInfos[0] = “fromEmaiAddress@xDomain.com”;
emailInfos[1] = “ccEmailAddress@yDomain.com”;
emailInfos[2] = “toEmailAddress@zDomain.com”;
emailInfos[3] = “Mail Subject”;
emailInfos[4] = “Mail Content”;
emailInfos[5] = new byte[Int64.MaxValue]; //Mail attachment


After extraction;

public class EmailInformation { → (1)
public string FromEmailAddress { get; set; } → (2)
public string CcEmailAddress { get; set; } → (2)
public string ToEmailAddress { get; set; } → (2)
public string Subject { get; set; } → (2)
public string Content { get; set; } → (2)
public byte[] Attachment { get; set; } → (2)
}

EmailInformation emailInfos = new EmailInformation();
emailInfos.FromEmailAddress = “fromEmaiAddress@xDomain.com”; → (3)
emailInfos.CcEmailAddress = “ccEmaiAddress@yDomain.com”; → (3)
emailInfos.ToEmailAddress = “toEmaiAddress@zDomain.com”; → (3)
emailInfos.Subject = “Mail Subject”; → (3)
emailInfos.Content = “Mail Content”; → (3)
emailInfos.Attachment = new byte[Int64.MaxValue]; → (3)


Generating Conditions

Complex condition is one of complexity concepts in a software. Blocks of codes become longer as count of conditions in those blocks increases. Therefore, reading these codes gets harder.

Any of those blocks can be shortened and made clearer by generating a method indicating the purpose of that block and then inserting that block’s code into this new method. With the help of this method further codes in the method can be decomposed depending on what you are branching.


Pattern

Create a new method and move condition into it
Create a method for each branch in the method

if(loanType == LoanTypes.Mortgage){
monthlyPayment = loanAmount * [(6.25 * (1 + 6.25) ^ 30 * 12) ]
/ [(1 + 6.25) ^ 30 * 12 – 1]
}
else if(loanType == LoanTypes.Vehicle){
monthlyPayment = loanAmount * [(5.64 * (1 + 5.64) ^ termsOfNMonths) ]
/ [(1 + 5.64) ^ termsOfNMonths – 1]
}


After extracting;

class LoanParameters {
public double LoanAmount {get; set;}
public double InterestRate {get; set;}
public int TermsOfNMonths {get; set;}
}

double CalculateLoanMonthlyPayment(LoanParameters parameters){ → (1)
double monthlyPayment = parameters.LoanAmount * [(parameters.InterestRate * (1 + parameters.InterestRate) ^ parameters.TermsOfNMonths)]
/ [(1 + parameters.InterestRate) ^ parameters.TermsOfNMonths – 1]

return monthlyPayment;
}

if(loanType == LoanTypes.Mortgage){
monthlyPayment = CalculateLoanMonthlyPayment(new LoanParameters(){ LoanAmount = loanAmount,
InterestRate = 6.25,
TermsOfNMonths = 30 * 12}); → (2)
}
else if(loanType == LoanTypes.Vehicle){
monthlyPayment = CalculateLoanMonthlyPayment(new LoanParameters(){ LoanAmount = loanAmount,
InterestRate = 5.64,
TermsOfNMonths = termsOfNMonths}); → (2)
}


Replacing Condition Branches with Methods

With parameterized method you are required to write block of codes for a numerous parameters. Preparing blocks of codes for each parameter is get ridden of by replacing parameter with explicit methods.


It is easier to read than an explicit method than a parameterized method. In a parameterized method, any developer needs to look through conditions to understand over-all logic.

It is better not to replace parameter with explicit methods if the parameter has probability to have more values.


Pattern

Generate a method for each parameter
Alter each condition branch with calls to new methods

Transaction _transaction = null;
Document _transactionDocument = null;

void initialize (objects[] args) {
if(args != null && args.Length > 0){
if(args[0] is Transaction){
_transaction = args[0];
}
else if(args[1] is Document){
_transactionDocument = args[1];
}
}
}


After extracting;

void setTransaction(object transaction){ → (1)
_transaction = transaction;
}

void setTransactionDocument(object document){ → (1)
_transactionDocument = document;
}

void initialize (objects[] args) {
if(args != null && args.Length > 0){
if(args[0] is Transaction){
setTransaction(args[0]); → (2)
}
else if(args[1] is Document){
setTransactionDocument(args[1]); → (2)
}
}
}



References

Refactoring: Improving the Design of Existing Code by Martin Fowler, Kent Beck (Contributor), John Brant (Contributor), William Opdyke, don Roberts

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.