<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
, rowsPerPage
printNumbers
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.