<ctrl + alt + shift>-T<alt>-ENTER
Download primeGenerator.zip from the Refactoring Activity 1-3 folder on the Moodle class page (right-click and save link to your directory for this class).
In a terminal window, navigate to that directory and run: unzip primeGenerator.zip. This will create a directory called primeGenerator.
Start IntelliJ and select: File -> New -> Project from Existing Sources.
Navigate to and select the primeGenerator folder that you just unzipped. Follow the Next buttons to complete the process for creating the project.
Verify that the project has the following three files:
Open up the “PrintPrimesTest” file, and expand the “import” section by clicking the plus button next to the line.
Place your cursor over the red “junit” text, then trigger the intention menu (alt-Enter) and select “add junit 4 to the classpath”. All the red marks should now have disappeared.
Create a git repository for the project from within IntelliJ: VCS -> Enable Version Control Integration then choose Git as the version control system.
Choose VCS -> Commit to create the first commit. Select the topmost checkbox to include all the files in the commit, type a Commit Message of “Initial Commit” then hit the Commit button.
Create a remote git repository for the project on GitHub from within IntelliJ (you will need to have an account): VCS -> Import into version control -> Share project on GitHub
Right-click on “PrintPrimesTest” (either the filename or the tab) and select: Run 'PrintPrimesTest' from the given menu. This will verify that everything is setup properly. Ask for help if the test fails.
Take a look at the PrimePrinter.java and its main method. This is the method we will be refactoring.
Take a look at the “gold” file. This contains the output of our method; our method produces this printed result. The tests simply compare the function’s output to this printout.
Take a look through the list of variable names. Note that for some variables, it is how the name is formatted rather than the name itself that needed to be improved (e.g., camel-case instead of all-caps).
| New Name | Current Name | Description |
|---|---|---|
numPrimes |
M |
number of primes to generate and print |
rowsPerPage |
RR |
number of rows to print per page |
colsPerPage |
CC |
number of columns to print per page |
ORDMAX |
Leave as is; we will come back to this one later | |
primes |
P |
list of primes |
pageNumber |
PAGENUMBER |
current page number in the printout |
pageOffset |
PAGEOFFSET |
offset into the primes array where the current page starts |
rowOffset |
ROWOFFSET |
offset into the primes array where the elements in the current row start |
col |
C |
current column in the printout |
candidatePrime |
J |
candidate prime number |
lastPrimeIndex |
K |
index into the prime array for the last computed prime |
possiblyPrime |
JPRIME |
boolean that indicates whether the candidate number (J) is “possibly prime” (false means definitely not prime) |
ORD |
Leave as is; we will come back to this one later | |
nextPrimeSquare |
SQUARE |
next possible prime square |
N |
Leave as is; we will come back to this one later | |
multiples |
MULT |
an array of multiples |
Rename each variable in PrimePrinter.java as follows:
<ctrl+alt+shift>-T to open the Refactor This menu.ENTER to make the change.Notice that all occurrences of the original variable name have been renamed.
Use Code -> Reformat Code to automatically fix the indentation and spacing.
The last thing to do is to move the variable declarations closer to where the variables are first used. For each variable declaration, do the following:
<alt>-ENTER to show available intentionsnumPrimes last, as the location of the other declarations limits our ability to move it earlier.Commit changes to your local GitHub:
Looking at the structure of the PrimePrinter code, we can see that it splits broadly into two parts:
while loop, which seems to do be doing the work of computing prime numbers.while loop (after pageNumber and pageOffset are initialized), which seems to have to do with printing the numbers.We want to isolate each loop by turning it into its own method. However, before doing that, we need to think about the variables being used. They fall broadly into three categories:
numPrimes, rowsPerPage, colsPerPage.ORDMAX, primes, multiples, candidatePrime, lastPrimeIndex, ORD, nextPrimeSquare, and maybePrime.pageNumber, pageOffset, rowOffset, col.We want the second set of variables to be local variables in a function called generatePrimes, which we will generate from the first while loop. We want the third set of variables to be local variables in a function called printNumbers, which we will generate from the second while loop.
Follow the steps below to refactor and extract the two main while loops into separate methods:
Extract the generatePrimes Method (Try 1)
while loop.<ctrl+alt+shift>-T to open the Refactor This menu.After this refactor, the first while loop and all its code should now be down at the bottom of the file in a method called “generatePrimes”. In its place should be the following function call (with possibly different parameter order):
generatePrimes(numPrimes, primes, lastPrimeIndex, ORD,
nextPrimeSquare, multiples, candidatePrime);
Undo and redo the method extraction a few times until you can see this is what happens.
Extract the generatePrimes Method (Try 2)
Although the above refactoring gave us the method we wanted, the variables that we want to be declared locally are instead being passed in as parameters. There are a couple ways to fix this, but the simplest is to redo the refactoring, this time making sure to include all the declarations for the variables we want to be local in the new method.
main.generatePrimes. They should be right above the first while loop, and the variables we want to be local to printNumbers should be directly above the second while loop. If not, move the variables to where they should be.while loop again, but this time be sure to include all the variable declarations above it, with the exception of numPrimes.Your line calling generatePrimes should now look like the following:
int[] primes = generatePrimes(numPrimes);
There are a couple things you should note about this second try at refactoring the first while loop:
generatePrimes method with a much simpler signature that requires the passing of just a single parameter: numPrimes.generatePrimes to have it return the the correct thing, the primes array.Extract the printNumbers Method
while loop in main, including all the variable declarations after the call to generatePrimes.<ctrl+alt+shift>-T) and extract the method.Commit your changes:
After the above refactoring, your main method should be short and clear as to what it aims to accomplish:
public static void main(String[] args) {
final int numPrimes = 1000;
int[] primes = generatePrimes(numPrimes);
printNumbers(numPrimes, primes);
}
The two methods created above are a good start: each handles a specific and disjoint set of variables. However, both methods are still quite large, and they deal with two mostly unrelated tasks. This suggests that the two methods really belong in two different classes. Let’s use our two methods as the starting point for creating these classes.
Create the PrimeGenerator class:
generatePrimes call in main.<ctrl+alt+shift>-T) and under Extract select Method Object.PrimeGenerator.OK.generatePrimes method to the extracted class.The new class was created as an inner class to the PrimePrinter class, but ideally we would like it to be its own class.
PrimeGenerator class from the first line of the class definition.<ctrl+alt+shift>-T) and select Move.PrimeGenerator to upper level.PrimeGenerator.java to the git repository.Take a look at the newly created PrimeGenerator class. We do not need both the invoke and the generatePrimes methods. Currently invoke is just a wrapper around the call to generatePrimes. To clean this up, we will inline generatePrimes and then rename invoke.
generatePrimes method.<ctrl+alt+shift>-T) and select Inline.All the code from generatePrimes should now be part of the invoke method, and the generatePrimes method should be gone.
invoke to generatePrimes.We are far from done with refactoring the PrimeGenerator class: It still consists of one large method, an we will need to do something about that. For now, though, let’s move on to the printNumbers method.
Create the NumberPrinter class
Back in PrimePrinter.java, take a moment to look at the parameters and variables at the top of printNumbers:
colsPerPage and rowsPerPage variables feel like they should be part of the initialization of the new class: When you create a number-printer, you should be specifying how many rows and columns you want it to print.primes and numPrimes parameters feel like elements that should be provided to printNumbers when it is invoked.pageNumber and pageOffset will likely end up being internal variables to the new class.To get the variables and parameters where they need to be in the new class, we will first turn colsPerPage and rowsPerPage into parameters, and then extract a parameter object (instead of a method object).
colsPerPage.<ctrl+alt+shift>-T) and extract it as a parameter.rowsPerPage as a parameter. At this point your printNumbers call should have four parameters in some order: numPrimes, primes, colsPerPage, rowsPerPageprintNumbers method call or definition.<ctrl+alt+shift>-T) menu.NumberPrinter.numPrimes and primes.The printNumbers method should now have three parameters: numPrimes, primes, and numberPrinter, which is a NumberPrinter object. And the call to printNumbers contains in it the creation of this new class.
Now we’ll change printNumbers to a method of the NumberPrinter class:
printNumbers call.<ctrl+alt+shift>-T) and select Convert To Instance Method.numberPrinter instance as the destination (and not the this/new NumberPrinter()..).Take a look at the NumberPrinter class. IntelliJ has “encapsulated” the two fields behind getter methods, which we do not really need. Inline each of these getter methods one at a time to remove them:
<ctrl+alt+shift>-T) and select Inline.Go back to PrimePrinter.java. The program is now nice and simple – it initializes a few parameters, creates a prime number generator and calls it, and creates a number printer and calls it with the primes. However, there is a bit more refactoring we can do to clean it up even more.
numberPrinter.primeGenerator.primes array in its definition and refactor to inline the variable, moving the construction of the array so that it now occurs inline as part of the printNumbers method call.printNumbers method to just print, and the generatePrimes method to just generate.One last time, run the tests to make sure that everything is still working.
Commit and push changes.
This activity continues in refactoring activity 2.