Refactoring Demonstration
Contents
Overview
This is not Lab 8; this is a demonstration of refactoring that you might find useful in completing Lab 8. Here is an example program that plays the Monty Hall game, where a contestant (the user) chooses which of three doors hides a prize. After their first guess, one of the two remaining doors are opened to show that the prize is not behind it. The contestant then has to decide to stick with their original choice or to switch to the remaining closed door. It turns out, statistically, you should always switch...
(Back to top)Phase 1: Reducing repeated code
In phase 1, we're looking for repeated code. Do you see any lines that are repeated throughout the program? The easiest one to spot is the code that prints a border of stars on lines 44, 53, 79, 129, and 181:
While that's only one line of code, it's got a bunch of stuff going on and it's
not the easiest to comprehend. So, let's make a function with the signature
void printBanner()
whose only job is to print that banner. We can
then replace each line in the original code with an invocation of
printBanner()
.
There's also repeated code when we print out the door graphic. With the exception of the first time we print the graphic, the code looks identical each time:
In the first instance on lines 60–63, we hardcoded the door labels so the first door said
1, the second 2, and the third 3. We can actually use the code above
in place of lines 60–63 since door1Label
is initialized to
"1", door2Label
is initialized to
"2", and door3Label
is initialized to
"3". So, let's make another function to print this out. We'll need to give it
the door labels, so those should be parameters. Here's a reasonable
signature: void printDoorGraphic(string door1Label, string door2Label, string door3Label)
.
After adding these functions, here's what the program looks like:
(Back to top)Phase 2: Modularizing code
Our program still has a lot of code in main that does lots of stuff at varying levels of detail. To help make this program more modular, let's think of what high level actions are taking place, and then for each of those what lower level actions are taking place, and so on. Here's one way to do it for this program:
- main
- run the game
- pick which door the prize will be behind
- display the welcome banner
- get the players first guess and process it
- prompt the user and read in their choice
- determine which door should be opened for the player
- check if the player wants to switch their choice
- figure out which door is closed (that's the one the player will be switching to)
- prompt the user and read in their decision
- if they do want to switch, make the switch
- check if the player won or not and let them know
- run the game
So let's make a function for each of these components! Here's the same set of steps, but with the description replace with functions:
main()
runGame()
chooseDoorWithPrize()
printWelcomeMessage()
runInitialChoice(...)
promptPlayerForDoorChoice()
chooseDoorToOpen(...)
runSwitchOrNot(...)
findClosedDoor(...)
playerWantsToSwitch(...)
makeSwitch(...)
runGameWonOrNot(...)
Here's what the code looks like once we have add those functions (note that some variables are pass by reference; I've only used that in cases where the function will modify those variables, and I make not of it in the function's comment):
(Back to top)Phase 3: Increasing reusability
In this third phase, we're looking for similar code (but not identical, like in phase 1) that is repeated that we might be able to make some kind of abstract function for. In this program, take a look at the lines where we change the door labels. We do this five times, and the code to do it looks pretty ugly. Each time, though, we do something a little different; the first time, we are changing the label for the door the player choose to a star; the second time, we are changing the label of the door we're opening for the user to a blank space; etc. Can we come up with a function that can take care of all these cases? You betcha! What we need is the number of the door whose label should change and what the new label should be (plus all the current door labels); here's the function:
So in this case, we did more than just copy and paste the code into a function. We really had to figure out how to modify the code so we could adapt it to all of the situations in which we used the original code. Here's what it looks like in action:
(Back to top)