Lab: Refactoring Code
Steven J Zeil
This lab will give you practice in refactoring existing Java code: modifying it in ways that have nothing to do with writing it in the first place or fixing bugs.
1 Set Up Your Project
-
By now, you should have set up your development environment.
You may reuse your project directory from that earlier lab or start a new project with the original set of files.
-
If you are starting with a fresh copy, set up a Java project with that directory using your chosen IDE.
- Make sure that the code compiles as it did before.
- Set up a run configuration using one of the provided test files and make sure that the program runs correctly.
2 Refactoring 1: Project Layout
As projects grow, we are seldom eager to keep everything in one flat directory structure. Instead, we try to separate the source code, any test data, and any files produced by compiling the source code into separate directories.
-
If you are using Eclipse, delete the project from the list in the Project or Package Explorer column. (Do not delete the files – just the project.)
-
Exit your IDE and do the remaining steps via operating system commands.
-
Delete, if present, the files/directories:
.project
,.classpath
, and.vscode/
.Note that all of these start with a ‘.’. which means that in Linux and MacOS these are considered “hidden” files that do not show up in normal directory listings. You can use “
ls -a
” at the command line to show these hidden files. -
Let’s isolate the source code. Create the nested directories
src/main/java
and move the.java
files inside there.src/main/java
is now our source code directory. Java projects can have multiple source code directories.If we were to compile this code from the command line, we would start by cd’ing to the source code directory and would issue the compilation command from there.
-
Try it: From a command line,
cd
tosrc/main/java
and give the commandjavac -g Highway.java
It should compile without errors. Then examine the directory. You should see the
.class
files produced by compiling Java code. -
To run the program, we either need to be in the source directory or supply a CLASSPATH to that source directory. There are two ways to modify the CLASSPATH. One is to set it as an environment variable or to supply the path info to a
-cp
command line parameter.cd
to the top of the project (the directory containingsrc/
) and give the command:java -cp src/main/java Highway testData/test000.dat
Then, to prove that the
-cp
was necessary, give the commandjava Highway testData/test000.dat
You can see that, without the
-cp
, the java engine does not know where to look to find the code. -
Delete the
.class
files from withinsrc/main/java
-
-
Let’s isolate the test data. Create the nested directories
src/test/data
and move the.dat
files inside there. Remove thetestData/
directory afterwards.Check your directory structure. Your project’s top-level should now have the
README.md
file, thesrc/
directory, and nothing else. If you have anything else, delete it. -
Open up your IDE, creating a Java project at your top directory (the one containing
src/
). Both Eclipse and VSCode are smart enough to recognize that your project’s source code directory issrc/main/java
. -
In your operating system, search the project directories for .class files.
Note that both IDEs will, by default, store the
.class
files in a separate directory from the source code.- Among other advantages, this makes cleaning up a project fairly easy, because you can safely delete the
bin/
directory at any time, knowing that the IDE can rebuild everything within it.
- Among other advantages, this makes cleaning up a project fairly easy, because you can safely delete the
-
If you are running Eclipse, right-click on the project name in the Package Explorer, and select
Build path
, thenConfigure Build Path
.The Build path in Eclipse controls a variety of crucial project settings, including how many source code directories you have and where they are, and where the compiled output code should be stored.
Go to the “Source” tab and examine your project setup. It should be obvious where Eclipse believes that your source code directory is. Can you also see where the output directory is set? Try changing the location of the output directory and observe the effects, both within Eclipse and at the Operating System level.
3 Refactoring 2: Repackaging the Code
-
Edit
Interval.java
and add the following line at the top of the file:package edu.odu.cs;
Save the file. Your IDE should now flag the code as erroneous, and if you examine the error message you will see that it is complaining about the location of the file being inconsistent with the package.
Remember that, in Java, packages mirror directories and vice versa.
-
Your IDE will offer to fix the problem for you. In Eclipse, click on the small red marker on the left edge of the source code. In VSCode, click on the offering line and then click Ctrl-. to request a “quick fix”.
-
Each IDE will offer to either change the package declaration to match the current directory location, or to move the file to a new directory to match its package declaration.
Select the option to move the file.
-
Examine the effects of this both in the IDE and at the operating system level. You should see a change in the directory structure under
src/main/java/
.
-
-
In the IDE, move the other
.java
files to the new package directory. Again you will see error messages showing up.This time, allow your IDE to change the package declaration to match the new location of the file.
The directories
src
,src/main
, andsrc/main/java
are part of the project structure.The directories
edu
,edu/odu
, andedu/odu/cs
are part of the actual Java source code. -
Verify that you can still run the program. Don’t forget that your test data is now inside
src/test/data/
. -
Exit your IDE for the moment and return to a command line.
-
cd
tosrc/main/java
(your source code directory). Look at the directory contents. You should have only a directory namedCS350
. -
Compile the code:
javac -g edu/odu/cs/Highway.java
We still compile the code from the source code directory, but now we indicate which code to compile by giving the path through the package directory structure.
-
cd
back to the top of the project and run the code:java -cp src/main/java edu.odu.cs.Highway src/test/data/test000.dat
Note the use of ‘.’ in the class name rather than ‘/’. We aren’t giving a path to a file here - we are giving the full name, including packages, of the compiled class that we want to run.
-
Delete the .class files in
src/main/java/edu/odu/cs/
.
-
4 Refactoring 3: Improving the Source Code
-
In your IDE, open
Highway.java
. Find the line that calls thesubtract
function. Highlight the name of that function by right-clicking on it or clicking and dragging your mouse across it. Now, let’s find its declaration.- In Eclipse, use the F3 key or right-click and select “Open Declaration”.
- In VSCode, use the F12 key or right-click and select “Go to Declaration”.
-
You should be looking at the declaration of the
subtract
function inRanges.java
. We are going to rename this function.-
Double-click or click-and drag your mouse to highlight the word “subtract” in the declaration.
-
If you are in Eclipse, right-click on the highlighted word and select
Refactor
, thenRename
. - If you are in VSCode, right-click on the highlighted word and select
Rename Symbol
. Change the name to “remove”.
Now look at
Highway.java
. Notice that it is marked in the IDE editor as changed but not saved. Look inside thewhile
loop in the functiondoIt
. You can see that the call tosubtract
has been replaced by the new name, even though we made the change in a completely different file. This was clearly smarter than a simple text search-and-replace.Save the changes to both files.
-
-
Again, look at
Ranges.java
, inside the newly renamedremove
function. According to the principles of Clean Coding, there’s too much going on inside this function body.Click-and-drag the mouse to highlight the 2nd
if
statement (and its body).- If you are in Eclipse, right-click on the highlighted text and select
Refactor
, thenExtract Method...
- If you are in VSCode, right-click on the highlighted text and select
Refactor...
, thenExtract to method
Name the new function “replaceIfOverlapping”.
You should see the if statement pulled out of the
remove
function and replaced with a call to areplaceIfOverlapping
function. - If you are in Eclipse, right-click on the highlighted text and select
-
In that new function, highlight the
if
statement body inside the{ }
and extract that as a new method named “replaceOverlappingInterval”. -
Look in the newly created function. Do you see two small
if
statements with nearly identical structure? Highlight the first of them, and extract as a new function named “addIfNonEmpty”. Notice that both blocks of nearly identical code are replaced by a call to the newly extracted function.You may want to rename the
Interval
parameter to this common function to something more general, like “interval”. -
Check to be sure that the program compiles and runs as before.
It should. The point of refactoring is to introduce useful changes without affecting the behavior of the code.