One of the key attributes towards code that is readable and easy on the eyes is code that is split into appropriately sized pieces. Code refactoring is does exactly that. It is very easy to write a program as one big piece of code. Of course, any program that grows becomes increasingly complicated and highly inefficient. If not controlled, it will soon reach a point where it is highly unreadable, extremely difficult to maintain & filled with bugs. Not to mention that it is inefficient too.
Refactoring code and breaking it down into smaller reusable chunks is the key. The objective is:
- To make code easier to read
- To make reusable components so that we can save on duplication of code. This will reduce the code count and make sure that any changes to the reused code are available everywhere.
- To lend a structure to the application. Tasks now have their own space.
- Build scalable and maintainable code.
- Build bug free code.
Let us look at an example.

Bad Code
This code is clearly written poorly. Its difficult to read. There aren’t good whitespaces. No consistency. Even the naming conventions are poor.
The fix would be :
- Break it down into different functions
- Separate tasks into their own files
- Name the different elements of the code properly.
This is how the code looks now. It has been broken down into different files.
main.cpp
#include <iostream> #include "MathOperations.hpp" #include "Choices.hpp" int main(int argc, const char * argv[]) { float number1 = 0.0; float number2 = 0.0; Choices selectedOption = CLEAR; float answer = 0; float integralAnswer = 0; while(EXIT != selectedOption) { //Welcome message std::cout<<"Welcome to Calculator Program"<<std::endl; std::cout<<"Choose between the following options"<<std::endl; std::cout<<"1. Add\n2. Subtract\n3. Multiply\n4. Divide\n5. Remainder\n6. Percentage"<<std::endl; //User choice std::cout<<"Choice: "; std::cin>>selectedOption; //Chance to enter first number std::cout<<"Number 1: "; std::cin>>number1; //Chance to enter second number std::cout<<"Number 2: "; std::cin>>number2; switch (selectedOption) { case ADDITION: answer = addition(number1, number2); std::cout<<"The addition of "<<number1<<" & "<<number2<<" = "<<answer<<std::endl; break; case SUBTRACTION: answer = subtraction(number1, number2); std::cout<<"The subtraction of "<<number1<<" & "<<number2<<" = "<<answer<<std::endl; break; case MULTIPLICATION: answer = multiplication(number1, number2); std::cout<<"The multiplication of "<<number1<<" & "<<number2<<" = "<<answer<<std::endl; break; case DIVISION: answer = division(number1, number2); std::cout<<"The division of "<<number1<<" & "<<number2<<" = "<<answer<<std::endl; break; case REMAINDER: integralAnswer = remainder((int)number1, (int)number2); std::cout<<"The remainder of "<<number1<<" divided by "<<number2<<" = "<<integralAnswer<<std::endl; break; case PERCENTAGE: answer = percentage(number1, number2); std::cout<<"The percentage of "<<number1<<" out of "<<number2<<" = "<<answer<<span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0" ></span><std::endl; break; default: break; } } return 0; }
Choices.hpp
#ifndef Choices_hpp #define Choices_hpp #include <stdio.h> #include <iostream> enum Choices : unsigned short int { ADDITION = 1, SUBTRACTION, MULTIPLICATION, DIVISION, REMAINDER, PERCENTAGE, CLEAR, EXIT}; typedef enum Choices Choices; std::istream& operator >>(std::istream &is, Choices& enumVar); #endif
Choices.cpp
#include "Choices.hpp" std::istream& operator >>(std::istream &is, Choices& enumVar) { unsigned short int intVal; is>>intVal; switch (intVal) { case 1: enumVar = ADDITION; break; case 2: enumVar = SUBTRACTION; break; case 3: enumVar = MULTIPLICATION; break; case 4: enumVar = DIVISION; break; case 5: enumVar = REMAINDER; break; case 6: enumVar = PERCENTAGE; break; default: enumVar = EXIT; break; } return is; }
MathOperations.hpp
#ifndef MathOperations_hpp #define MathOperations_hpp #include <stdio.h> //Addition float addition(float number1, float number2); //Subtraction float subtraction(float number1, float number2); //Multiplication float multiplication( float number1, float number2); //Division float division(float number1, float number2); //Remainder int remainder(int number1, int number2); //Percentage float percentage(float number1, float number2); #endif
MathOperations.cpp
#include "MathOperations.hpp" //Addition float addition(float number1, float number2) { return number1 + number2; } //Subtraction float subtraction(float number1, float number2) { return number1 - number2; } //Multiplication float multiplication( float number1, float number2) { return number2 * number1; } //Division float division(float number1, float number2) { if (number2 > 0) { return number1 / number2; } return 0.0; } //Remainder int remainder(int number1, int number2) { return number1 % number2; } //Percentage float percentage(float number1, float number2) { if (number2 > 0) { return (number1 / number2) * 100.0; } return 0.0; }
Let us look at how this looks for Swift.
main.swift
import Foundation var number1 : Float = 0.0 var number2 : Float = 0.0 var selectedOption : Choices = Choices.CLEAR var answer : Float = 0.0 var integralAnswer : Int = 0 func readNumbers(One firstNumber : inout Float, Two secondNumber : inout Float) { //Chance to enter first number print("Number 1: \n") firstNumber = Choices.inputNumbers() //Chance to enter second number print("Number 2: \n") secondNumber = Choices.inputNumbers() } while(Choices.EXIT != selectedOption) { //Welcome message print("Welcome to Calculator Program") print("Choose between the following options") print("1. Add\n2. Subtract\n3. Multiply\n4. Divide\n5. Remainder\n6. Percentage") //User choice print("Choice: \n") selectedOption = Choices.inputChoices() switch (selectedOption) { case Choices.ADDITION: readNumbers(One: &number1, Two: &number2) answer = addition_of(_value: number1, with_value: number2) print("The addition of \(number1) & \(number2) = \(answer)") break case Choices.SUBTRACTION: readNumbers(One: &number1, Two: &number2) answer = subtraction_of(_value: number1, from_value: number2) print("The subtraction of \(number1) & \(number2) = \(answer)") break case Choices.MULTIPLICATION: readNumbers(One: &number1, Two: &number2) answer = multiplication_of(_value: number1, with_value: number2) print("The multiplication of \(number1) & \(number2) = \(answer)") break case Choices.DIVISION: readNumbers(One: &number1, Two: &number2) answer = division_of(_value: number1, by_value: number2) print("The division of \(number1) & \(number2) = \(answer)") break case Choices.REMAINDER: readNumbers(One: &number1, Two: &number2) integralAnswer = remainder_of(_value: Int(exactly:number1)!, <span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0" ></span>divided_by_value: Int(exactly: number2)!) print("The remainder of \(number1) divided by \(number2) = \(integralAnswer)") break case Choices.PERCENTAGE: readNumbers(One: &number1, Two: &number2) answer = percentage_of(_value: number1, with_respect_to_value: number2) print("The percentage of \(number1) out of \(number2) = \(answer)") break default: selectedOption = .EXIT break } }
Choices.swift
import Foundation enum Choices { case ADDITION, SUBTRACTION, MULTIPLICATION, DIVISION, REMAINDER, PERCENTAGE, CLEAR, EXIT} //CLI Reading Capability extension Choices { static func inputChoices() -> Choices { let ip : String? = readLine() let choice : String = String(ip!) switch choice { case "1": return .ADDITION case "2": return .SUBTRACTION case "3": return .MULTIPLICATION case "4": return .DIVISION case "5": return .REMAINDER case "6": return .PERCENTAGE default: return .EXIT } } static func inputNumbers() -> Float { let ip : String? = readLine() let numberFormatter = NumberFormatter() let number = numberFormatter.number(from: ip!) let num : Float? = number?.floatValue return num! } }
MathOperations.swift
import Foundation //Addition func addition_of(_value number1 : Float, with_value number2 : Float) -> Float { return number1 + number2; } //Subtraction func subtraction_of(_value number2 : Float, from_value number1 : Float) -> Float { return number1 - number2; } //Multiplication func multiplication_of(_value number1 : Float, with_value number2 : Float) -> Float { return number2 * number1; } //Division func division_of(_value number1 : Float, by_value number2 : Float) -> Float { if (number2 > 0) { return number1 / number2; } return 0.0; } //Remainder func remainder_of(_value number1 : Int, divided_by_value number2 : Int) -> Int { return number1 % number2; } //Percentage func percentage_of(_value number1 : Float, with_respect_to_value number2 : Float) -> Float { if (number2 > 0) { return (number1 / number2) * 100.0; } return 0.0; }
Discussion on Swift Extensions
As we can see that most of the code in Swift is very similar to C++. Most of the differences are basic syntactic differences. However, there is 1 feature of Swift that greatly aids code refactoring that I would like to talk about, Extensions.
Extensions allow us to add new functionality to the existing type. As the name says the type is extended. This allows us to add changes to a type in a consistent & clearly demarcated way. Developers can now neatly separate newly added components. This greatly helps in understanding the evolution of types.
“This is often referred to as versioning.”
Extensions can be used in the following ways to implement code refactoring:
- Different sections of a type reside in their own extensions
- Changes made to a type are made by keeping them in their own extensions
- Step by step build up of code is done by representing each step as an independent extension. This gives clarity on how a certain end result was achieved.
Conclusion
As we can see from the sample code above (for both C++ & Swift) the program is much more readable. Code is carefully compartmentalised. Its a lot easier to read. It is a lot easier to scale too.
The reader may point out that the amount of code to achieve the same result is significantly higher, that however is a small price to pay in the long run. The biggest advantage is the scalability & the ease with which it can be done. Simply breaking code down into separate files & functions makes a huge difference. Here are some other benefits:
- Individual files can be modified. This means one can now have a team working on different parts of the code.
- Code is less cluttered. Changes are now spread across files & are easier to track.
Pingback: Programming Style Guide – The Need for programming standards | Arun Patwardhan's Blog