This programming assignment is the complement of the Design Document 1 assignment. The general task was already explained; this assignment description provides additional details and tips for the programming aspects of the redesign.
You must identify the behavior associated with each class (i.e.,
the behavior exhibited by instances of the class) and move that behavior
from the standalone static methods in Functions.java
to
(static or non-static, as appropriate) methods defined within the class.
For this assignment, you will not add new functionality (aside from some
accessor/mutator methods, only as needed). This is to be done in two forms:
an UML design document (design document 1 assignment) and a
refactoring of the provided source code that executes as before.
You are encouraged to develop the UML design document first, however, you are also welcome to simultaneously work on the code refactoring. If you do start refactoring the code, you are encouraged to implement the refactoring incrementally so that your refactored program executes properly at each step.
Functions.java
to the appropriate class
After completion of the first few lab assignment(s) for this course, you should be comfortable with the basics of building and executing Java programs on both the command-line and in IntelliJ.
The provided source code relies on the processing.org API for the graphical interface. To use this API external to the Processing environment, you will need a jar file *please use the provided file processing-experimental.jar file for both compilation and execution. *Note we are using a modified Processing jar that supports more recent versions of java, so please be sure to use the jar included in the zip file for this assignment.*
Without introducing a build tool, the most direct approach to building and executing the program is as follows.
To compile all source files, execute the following in the directory holding the files.
javac -cp ${CLASSPATH}:processing-experimental.jar *.java
To execute the program, execute the following in the directory
holding the files. This also assumes that the world.sav
file, the imagelist
, and the images
directory
are also in the same directory.
java -cp ${CLASSPATH}:processing-experimental.jar VirtualWorld
When compiling and running Java programs, the Java compiler/run-time
will search various locations for classes that your program uses. This
allows, for instance, multiple Java programs to share libraries. The
CLASSPATH
environment variable is how one specifies the
locations to search for such classes. You can simplify the above
commands by setting the CLASSPATH
in your shell
configuration files (typically in .mybashrc
on the CSL
machines).
The following steps will guide you through the creation of a new project to build and execute the given program. (We are going through the steps as part of learning the tool for your future use.)
.java
source files to the project. This is
most directly done via a graphical interface by selecting and
dragging (or copying, if you prefer) the files to the src
directory (folder) in the IntelliJ project view.
+
to add a new configuration. Select Application
,
enter Basic
(for instance) as the name, and enter
VirtualWorld
as the main class.
(You might add additional configurations with -fast
,
-faster
, or -fastest
as the respective program
argument later.)
+
to add a new JAR. In the file selection
dialog, find and select the
processing-experimental.jar that
you should have downloaded previously in the project zip file.
You should now be able to build and execute the program. Executing the program, however, will not load the proper resources for the graphical interface.
To run the program properly, copy the world.sav
file, the
imagelist
, and the images
directory to the
project folder in the project view.
That was a lot of steps. If you got stuck and something is not working, just ask for assistance in office hours or lab. Or take the opportunity to ask another student in the CSL (my experience is that the majority of students in the department are kind and want to help others succeed).
You must refactor the methods from the Functions
class
to move them into the appropriate classes as previously discussed. As
each method is moved, you will need to make modifications to the code
that uses the method.
Your refactoring should mirror the work done for your design document (UML diagram) augmented with feedback from your instructor.
Your refactoring must not add or remove any functionality. Your refactoring may add accessor/mutator methods, but only as needed. The resulting program must work as before.
It is not sufficient to simply move the static methods from
Functions
to the other classes and then continue to invoke
them as public static methods. For instance, if you determine that a
method works primarily on data within an Entity
object, then
the method must be made non-static and the explicit Entity
argument will be replaced by the implicit this
. This
modification will necessitate appropriate changes to the invocation of
the method.
As an example, moving the following (fake) method into Entity
will change it as shown.
class Functions { public static void turnAround(Entity entity, int numRotations) { ... entity.id ... } ... // invocation of turnAround turnAround(entity, 20); }
becomes
class Entity { public void turnAround(int numRotations) { ... this.id ... } ... // invocation of turnAround entity.turnAround(20); }
You can use the compiler (on the command-line or in the IDE) to help you with your refactoring. In particular, as you make changes, the compiler will flag now invalid uses of moved methods. This serves two purposes. The first, and arguably most important, is gaining an understanding of the error messages that the compiler reports and the reasons for such error messages. Nobody enjoys seeing error messages, but quickly interpreting and addressing such errors will improve your workflow.
The second purpose for using the compiler as an aid is that it can quickly identify all parts of a code base affected by a change. This is incredibly beneficial when working with unfamiliar code. (Many IDEs also provide similar support even without explicitly compiling.)
Consider the following more specific tips.
WorldModel
relies on Entity
). Start
your refactoring by moving methods into those classes that depend on
the fewest other classes.
private
. Compile the program
to determine which methods attempt to access these private attributes.
Refactor > Move
feature. Though this feature will not
make all of the necessary modifications, it will help with the task.
Be ware that overuse of this feature may interfere with the learning
objectives; but using this feature after manually moving a few methods
will save you time.
static
), remove the
target object from the parameter list (and change all uses within the
method to this
, implicitly or explicitly). Compile the
program to determine where the method was invoked.
With the exception of some constant (static final
) values,
all data attributes should be private
and, when possible,
final
. (Point
is the exception to this since
each value acts as a constant value akin to an integer.)
Methods should also be private
unless public
access is necessary (i.e., it is used outside of the defining class).
For this project, every method should be either private
or public
(it is often better to avoid the default of
package-protected).
Your submission of your refactoring will consist of the following files (this is the same set of files with which you started, but modified).