This is a beginner level tutorial on refactoring some old code I wrote a while ago. If you want to submit a homework assignment for a code review to be featured on Pragmatic Ways, you can join our Facebook Group and post your suggestion: Software Engineering Mastermind Group
Note: This program is written in C++, but the language or technology doesn’t really matter here. We’re not focusing on C++ specific details, we’re focusing on fundamental programming concepts to writing cleaner code.
Also note: This is a very basic example of refactoring with only a few tips on code cleanup. This example could be taken much farther, but to do so would require covering more advanced concepts that were not yet introduced prior to this assignment. This is a tutorial with a target audience for beginners who are starting out learning how to code.
How to refactor code
- Buy this book
- Read this book
- Master this book
- Master your career
- Master your life
Program Requirements
Joe’s Pizza Palace needs a program to calculate the number of slices a pizza of any size can be divided into. The program should also report the number of pizzas someone should order for a party. Assume each person at the party will eat 3 slices each. The program should prompt the user for the diameter of the pizzas they wish to order and the number of people who will be at the party. The program should then calculate and display the number of slices per pizza and the number of pizzas needed for the party.
- A slice must have an area of 14.125 inches
- Number of slices per pizza is the area of the pizza divided by the area of a slice
- Area of a pizza is calculated with Area = PI * r^2 where PI = 3.14159 and r is the radius of the pizza
- The number of slices should be fixed point and rounded to one decimal place
- PI must be a named constant
(Note: these program requirements came from home assignment in the book Starting Out with C++: From Control Structures through Objects)
Original Source Code
include
include
include
using namespace std;
int main() {
const double PI = 3.14159,
SLICE_AREA = 14.125;
double pizzaRad,
pizzaArea,
numSlicesPerPizza;
int slicesPerPerson = 3,
numPeople,
numPizzasNeed,
pizzaDi;
<code>// prompt user for pizza and party info cout << "What size pizza would you like? (Enter the inch diameter of the pizza)\n"; cin >> pizzaDi; cout << "How many people are at the party?\n"; cin >> numPeople; // calculations pizzaRad = pizzaDi / 2; pizzaArea = PI * pow(pizzaRad, 2.0); numSlicesPerPizza = pizzaArea / SLICE_AREA; numPizzasNeed = ((numPeople * slicesPerPerson) / numSlicesPerPizza) + 1; // display the results. cout << endl; cout << "Pizza Size: " << right << setw(10) << pizzaDi << endl; cout << "Number of slices: " << right << setw(4) << setprecision(1) << fixed << showpoint << numSlicesPerPizza << endl; cout << "Number of people: " << right << setw(4) << numPeople << endl; cout << "Number of pizzas: " << right << setw(4) << numPizzasNeed << endl << endl; system("pause"); return 0;</code>
}
Code language: PHP (php)
Refactoring Efforts
Now let’s see how we can refactor this code and clean some of it up. Before we actually dive into the implementation of things, let’s start by listing some of the issues we see with this program.
- The
main
function is handling 100% of the logic. It would be better to separate this logic into multiple functions that each handle a specific piece of the logic - It’s declaring all of the variables at the beginning, when many of them are only used once or twice for a specific purpose. We should reduce the scope of these variables to the lowest possible visibility
- The variable names aren’t terrible, but could be a little more explicit
Let’s start by extracting some of the logic from the main
function into separate functions.
Extracting the user input
The first actual piece of logic that’s happening is getting the user’s input for the pizzaDi
and numPeople
variables. Let’s move these into their own functions.
int getPizzaDiameterFromUser() {
int pizzaDiameter;
cout << "What size pizza would you like? (Enter the inch diameter of the pizza)\n"; cin >> pizzaDiameter;
return pizzaDiameter;
}
int getNumberOfPeopleFromUser() {
int numberOfPeople;
cout << "How many people are at the party?\n"; cin >> numberOfPeople;
return numberOfPeople;
}
int main() {
…
int pizzaDiameter = getPizzaDiameterFromUser();
int numberOfPeople = getNumberOfPeopleFromUser();
…
}
Code language: JavaScript (javascript)
Here I removed the declaration of the variables from the top of the function to declaring and initializing them for when I actually need to. We should always be focusing on declaring variables as close to their initialization and use as possible, and by extracting out this logic into a separate function, I’m now able to declare and initialize these variables on the same line in the main function.
Note: I also made the variables names a tiny bit more explicit. It’s not a huge difference, but the tiny bit of more information just helps with the readability of the code.
Now before we move on, hopefully you’ve noticed something about these two functions. Go ahead and take another look at these new functions we just created. Do you see it? Did you notice how these 2 functions are almost identical? The logic between both functions are nearly identical, and this is considered duplicate code. Let’s clean up this code duplication before moving forward by creating a new function.
int getUserInput(string message) {
int userInput;
cout << message; cin >> userInput;
return userInput;
}
int getPizzaDiameterFromUser() {
return getUserInput("What size pizza would you like? (Enter the inch diameter of the pizza)\n");
}
int getNumberOfPeopleFromUser() {
return getUserInput("How many people are at the party?\n");
}
Code language: JavaScript (javascript)
Now we’ve taken the duplicate code from the getPizzaDiameterFromUser()
and getNumberOfPeopleFromUser()
functions and extracted out what’s common between the two into the separate, generic function getUserInput()
.
Extracting the calculations
Next we’ll move the calculations into their own functions. There’s a lot of implementation logic and their required variables in the main
function that really doesn’t need to be there. The main
function is a high-level component, when things like the PI
constant and pizzaArea
variable are lower level implementation details.
It’s always best to hide the low level implementation details into lower level functions. This makes it easier for others to quickly scan the code from a high overview.
Realistically, if we look at our requirements again, the only things we need to do are to “calculate and display the number of slices per pizza and the number of pizzas needed for the party.” That’s all we need to do from a high level overview, so let’s keep our main
function at a high level overview and only show those details.
int main() {
double numberOfSlicesPerPizza = calcNumSlicesPerPizza();
int numPizzasNeeded = calcNumPizzasNeeded(numberOfSlicesPerPizza);
displayResults(numberOfSlicesPerPizza, numPizzasNeeded);
return 0;
}
Code language: JavaScript (javascript)
Now anyone wanting to see what our program does can quickly and easily look at the main
function and understand what this program does. If they want to see how it’s actually doing it, they can dig further into the lower level implementation details. Let’s build those out now to see what they’d look like.
Calculate the number of slices per pizza
What do we actually need to implement this function? From our requirements, we see that the “number of slices per pizza is the area of the pizza divided by the area of a slice.” We’re also already told that “a slice must have an area of 14.125 inches.” So let’s keep this function as simple as possible, and just follow what the requirements tell us.
double calcNumSlicesPerPizza() {
double pizzaArea = calculatePizzaArea();
const double SLICE_AREA = 14.125;
return pizzaArea / SLICE_AREA;
}
Code language: JavaScript (javascript)
What’s nice about this function is we isolated the SLICE_AREA
constant to just the 3 lines of code that actually need it. It’s now completely hidden from the main
function, just the way it should be. We don’t even need to know about the calculatePizzaArea()
details here, all we care about in the calcNumSlicesPerPizza()
function is that we are getting the area of a pizza.
Let’s now dive deeper and build out this calculatePizzaArea()
function (which quite honestly, most of the work was already completed earlier!).
What do we need to calculate the area of a pizza? Our program requirements basically just tell us that the area of a pizza is calculated the same way as the area of a circle, giving us the constant of PI = 3.14159
. Then we already got our diameter from the user before, so let’s move that function in here then convert the diameter into the radius.
double calculatePizzaArea() {
int pizzaDiameter = getPizzaDiameterFromUser();
double pizzaRadius = pizzaDiameter / 2;
return calculateCircleArea(pizzaRadius);
}
Code language: JavaScript (javascript)
The calculatePizzaArea()
function now takes care of getting the pizzaDiameter from the user input, then converting that diameter into the radius, then just simply calls a calculateCircleArea()
function to return the area of a circle, since that’s what the area of a pizza is anyways.
double calculateCircleArea(double radius) {
const double PI = 3.14159;
return PI * pow(radius, 2.0);
}
Code language: JavaScript (javascript)
Creating a function specifically for calculating the area of a circle makes our code a lot more flexible than if I just included the area function inside of the calculatePizzaArea()
function itself. Now, for whatever reason, if our program elsewhere needs to calculate the area of a circle, it can just use this simple function without needing to get all the details about getting a pizza’s diameter.
Calculate the number of pizzas needed
This calculation should be much simpler. We can now just take the numberOfSlicesPerPizza
we calculated above and pass that to a separate function which does the rest of the calculations that we need.
int calcNumPizzasNeeded(double numberOfSlicesPerPizza) {
int slicesPerPerson = 3;
int numberOfPeople = getNumberOfPeopleFromUser();
return ((numberOfPeople * slicesPerPerson) / numberOfSlicesPerPizza) + 1;
}
Code language: JavaScript (javascript)
Here, we also moved the assumed slicesPerPerson
variable directly into this function, since before it used to be unnecessarily defined at the top of the program.
Extracting the display
Now we have all our data, we just simply need to display the results. When I originally wrote this assignment, I guess I was trying to be cute and add a little flair and extra unnecessary data to the output. But if we look at our requirements, all we need to do is “display the number of slices per pizza and the number of pizzas needed for the party” and then to make sure the “number of slices should be fixed point and rounded to one decimal place.” So let’s just keep it simple this time.
void displayResults(double numberOfSlicesPerPizza, int numPizzasNeeded) {
cout << endl;
cout << "Number of slices per pizza: "
<< setprecision(1) << fixed << showpoint << numberOfSlicesPerPizza << endl;
cout << "Number of pizzas needed: "
<< numPizzasNeeded << endl << endl;
}
Code language: JavaScript (javascript)
Now, if we put it all together, our entire program looks like this.
include
include
include
using namespace std;
int getUserInput(string message) {
int input;
cout << message;
cin >> input;
return input;
}
int getPizzaDiameterFromUser() {
return getUserInput("What size pizza would you like? (Enter the inch diameter of the pizza)\n");
}
int getNumberOfPeopleFromUser() {
return getUserInput("How many people are at the party?\n");
}
double calculateCircleArea(double radius) {
const double PI = 3.14159;
return PI * pow(radius, 2.0);
}
double calculatePizzaArea() {
int pizzaDiameter = getPizzaDiameterFromUser();
double pizzaRadius = pizzaDiameter / 2;
return calculateCircleArea(pizzaRadius);
}
double calcNumSlicesPerPizza() {
double pizzaArea = calculatePizzaArea();
const double SLICE_AREA = 14.125;
return pizzaArea / SLICE_AREA;
}
int calcNumPizzasNeeded(double numberOfSlicesPerPizza) {
int slicesPerPerson = 3;
int numberOfPeople = getNumberOfPeopleFromUser();
return ((numberOfPeople * slicesPerPerson) / numberOfSlicesPerPizza) + 1;
}
void displayResults(double numberOfSlicesPerPizza, int numPizzasNeeded) {
cout << endl;
cout << "Number of slices per pizza: "
<< setprecision(1) << fixed << showpoint << numberOfSlicesPerPizza << endl;
cout << "Number of pizzas needed: "
<< numPizzasNeeded << endl << endl;
}
int main() {
double numberOfSlicesPerPizza = calcNumSlicesPerPizza();
int numPizzasNeeded = calcNumPizzasNeeded(numberOfSlicesPerPizza);
displayResults(numberOfSlicesPerPizza, numPizzasNeeded);
return 0;
}
Code language: PHP (php)
Did you know?
Before we wrap up, I want to let you in on a little secret. I wasn’t born a naturally clean coder. In fact, I used to write pretty ugly code, that is until I picked up this book! If you want to learn how to write clean code, then you NEED to get yourself a copy of the Clean Code book by Robert Martin.
Conclusion
Let’s recap a few of the high-level details of what we accomplished in this refactoring tutorial.
- We extracted lower-level implementation details from the higher-level functions by creating separate functions
- We moved variable declarations and initializations closer to where they’re actually used, thus reducing the need for globally scoped variables at the top of the
main
function - We renamed some variables, giving them a little bit more explicit description to their intent and meaning
If you liked this tutorial and would like to get some feedback on your own assignments, feel free to join our Facebook group and submit your request! Click here to join the Software Engineering Mastermind Group!