Refactoring code for a pizza ordering service


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.

best book on refactoring code

How to refactor code

  • Buy this book
  • Read this book
  • Master this book
  • Master your career
  • Master your life

>>> Click here to purchase the Refactoring Book

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 ObjectsRefactoring code for a pizza ordering service 1)

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;

	// 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;
}

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.

  1. 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
  2. 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
  3. 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();
  ...
}

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");
}

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;
}

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;
}

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);
}

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);
}

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;
}

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;
}

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;
}

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.

>>> Get your copy of the Clean Code book today!

Refactoring code for a pizza ordering service 2

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!

Adam Allard

Father of 2, husband of 1, developer of many. I'm a Software Engineer for Northrop Grumman creating web applications for the DoD. Currently I'm primarily working with Java and Angular based applications, although I have some years of experience in various languages and frameworks from previous jobs where I dabbled with Python & Django, C# & Xamarin, PHP, and Bootstrap. My hobbies include time with my family, wondering when the Green Bay Packers will win their next Super Bowl, drinking over-priced beer, and of course learning and teaching.

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Content