$29
PROGRAMMING II
Department of Computer Sciences
Pair Programming is NOT allowed for this assignment. You must complete this work individually.
This assignment involves reorganizing the code from your P4 Swim Sim (Exceptional) program to
make use of inheritance, and then adding support for changing amounts of food. This food will be
randomly added to the top of your simulation, and will then be removed from the simulation as it is
eaten by fish. This is your final SwimSim-based assignment for this course, so be sure to keep it as
clean and clear as possible.
Objectives and Grading Criteria
The goals of this assignment include refactoring existing code to make use of inheritance for code
clarity and to reduce redundancy. You’ll make use of inherited fields and methods, explicitly calling
constructors of a super class, and overriden methods. In addition to this refactoring, you’ll also be
adding support for dynamic numbers of Food objects.
Grade Breakdown:
25
points
5 zyBooks Tests: automated grading test results are visible upon submission, and allow
multiple opportunities to correct the organization and functionality of your code. Your
highest scoring submission prior to the deadline will be recorded.
25
points
5 Hidden Tests: automated grading tests are run after the assignment’s deadline. They
check for similar functionality and organizational correctness as the zyBooks Tests. But
you will NOT be able to resubmit corrections for extra points, and should therefore
consider and test your own code even more thoroughly.
P5: SWIMSIM (INHERITANCE)
LECTURE NOTES
GETTING STARTED
0. If it’s not already there, open the project containing your P4 code in Eclipse. Make a copy of this
project and name this copy whatever you’d like, although P5SwimSim would be a descriptive
choice.
REORGANIZING DATA AND CONSTRUCTORS
1. We are going to make use of inheritance to help make our code less redundant. Let’s start by
creating a new base class called SimObject that our Fish, Food, and Hook classes can all be
derived from. We’ll start by adding two protected int fields for the objects’ x and y positions to
this base class. Remove the old x and y fields from the Fish, Food, and Hook classes to ensure
that they are now using these inherited fields instead of the old ones. After this is working,
remove the PImage and PApplet fields from our old classes, and use the protected fields inherited
from our new SimObject class instead.
2. Since all of the SimObjects in our simulation (including all Fish, Food, and Hook objects) are all
making use of the same PApplet reference, it seems wasteful to have some many copies of the
same reference. Let’s consolidate these references by making the PApplet field in our SimObject
class static. This single static “class field” is shared by any/all SimObjects. We used to pass this
reference into every single new object upon construction. Let’s now change that by adding the
following mutator method to our SimObject class:
This method should be called once, from the beginning of our SwimSimulation classes
constructor.
3. Next we’ll create two SimObject constructors: the first takes only a string image path parameter,
and the second takes this same string followed by additional int x and int y parameters (in that
order). Notice that since the setProcessing() method takes care of initializing the PApplet
reference for all objects, that we don’t need to worry about doing this on a per-object basis. We
will however watch out for attempts to construct SimObjects without first calling setProcessing()
to set this reference to a non-null value. At the beginning of each SimObject constructor, check
whether the PApplet class field is null. When a null PApplet reference is found, your code should
throw an IllegalStateException with the message: “SimObject.setProcessing() must be called
before constructing any SimObjects.” When a non-null PApplet reference is found, the SimObject
constructor should proceed to initialize its fields by 1) loading a PImage corresponding to the
requested image path, and 2) initializing its position to the specified parameter values, or for the
one-argument constructor: to a random position within the bound of the processing display.
1
2
// initialize the PApplet reference that is used by all SimObjects
public static void setProcessing(PApplet processing)
4. In order for your Fish, Food, and Hook classes to make use of these constructors, they’ll have to
be called explicitly. While revising these constructors’ definitions, be sure to remove the PApplet
processing fields from their parameter lists. After these constructors have been revised, make
sure that they are being called properly from the SwimSimulation class. Your program should
now be back in working order. If not, be sure to fix any existing problems before you proceed to
the next step.
GROUPING COMMON FUNCTIONALITY
5. The distanceTo() methods in our Fish and Food classes are virtually identical, and this
functionality seems like it could be useful from a variety of different SimObject type objects. So
let’s move this definition from the Fish and Food classes into the SimObject class.
6. Although they are implemented in different ways, a common feature of our SimObjects is their
ability to update themselves. To help us make use of this common ability, we’ll add an empty
update method definition to the SimObject class, and have our Fish, Food, and Hook classes
override this method’s implementation.
7. A common ability among our Fish and Hook classes (and possibly other SimObjects in the future)
is to try to interact with another object. So let’s add the following empty method definition to
our SimObject class:
Then we’ll refactor the tryToEat() and tryToCatch() methods in our Fish and Hook classes to
instead override this new tryToInteract() method. Note that within this tryToInteract() method,
we’ll need to check whether the SimObject that we are trying to interact with is of the correct
type: Fish should only eat Food objects, and the Hook should only catch Fish. Updating your
references in the SwimSimulation class from tryToEat() and tryToCatch() to
tryToInteract() should bring your code back to working order.
Note that your SwimSimulation does not need to worry about the types of objects
that it is attempting to help interact: it should call tryToInteract() on every
SimObject (not just Fish and Hook) and with every SimObject as an argument of a
different call (not just passing Food to Fishes or Fishes to the Hook). This simplifies
the code in your SimObject, and also leaves us open to define additional kinds of
interactions between objects within their respective classes in the futre.
USING SIMOBJECTS FROM SWIMSIMULATION
1 public void tryToInteract(SimObject other) { }
8. Instead of managing separate arrays of Fish and Food objects (not to method the Hook), let’s
group all of these objects into a single ArrayList. Remove the Fish and Food array fields from
your SwimSimulation class, and replace them with an ArrayList of SimObjects. If your file
loading code relies on these arrays, your can either 1) refactor your file loading code to use the
ArrayList field directly, or 2) pass local array references into any methods that need that access,
and then later copy their contents into your ArrayList. You can either leave your hook reference
in place to help handle mouse clicks, or you can iterate through the ArrayList to find the Hook
object each time the mouse is clicked.
DYNAMICALLY ADDING AND REMOVING FOOD
9. At the beginning of our SwimSimulation’s update method, let’s add a 1 in 20 chance (5% of
updates) of creating a new Food object at a randomly chosen position along the top edge of our
display. Notice how much easier this is to implement when we make use a dynamically resizing
ArrayList. With the only Food[] organization, we’d need to create a new bigger array, copy
reference from the old one into the new one, and then store the additional reference to the newly
created Food object.
10. To help offset this influx of new Food objects, let’s start removing the Food objects that are
getting eaten by Fish. But let’s implement this functionality in a general way that allows us to
remove any kind of SimObject more easily in the future. Add a boolean protected instance field
to the SimObject class to help track whether each object needs to be removed from the
simulation. This field should be initialized to false (meaning that it does not need to be removed
yet), and will then be set to true when an object needs to be removed. Also add a public accessor
method to the SimObject class called shouldBeRemoved() that returns the value of this boolean
field. We’ll have the SwimSimulation object take care of removing objects that should be
removed in the next step. But before we get to that, go to your Food class’s getEaten() method
and make sure that a result of calling any food object’s getEaten() method is that their
shouldBeRemoved() method begins to return true instead of false. Removing the code from
getEaten() that was moving eaten food objects to a random position along the top edge of the
display, will help you see whether your solution to the next step is working or not.
11. Our final step is to remove all SimObjects that should be removed (according to their
shouldBeRemoved() method) from our simulation at the end of the SwimSimulation’s update()
method. Take some care in changing the contents of this ArrayList while iterating through it.
The code snippet below demonstrates a common problem, that you’ll want to be sure to avoid in
your solution. Trace through this code, and be certain that you understand how to avoid this
problem before completing your P5 Swim Sim implementation.
1
2
3
4
// initialize list to contain the letters: h, i, p, p, o, p, o, t, a, m, u, s
ArrayList<Character list = new ArrayList();
String string = "hippopotamus";
for(int i=0;i<string.length();i++)
12. Congratulations on finishing this final SwimSim-based CS300 assignment! After verifying that
your work is correct, and written clearly in a style that is consistent with the course style guide,
you should submit your work through zybooks. Note that if your classes contain extra fields
beyond those described in this write-up, that may cause problems for some of our tests. If the
automated tests detect defects in your code, you may fix those and re-submit for full credit,
although you will need to wait an hour between submissions. The most recent of your highest
scoring submissions prior to the deadline of 17:00 on Thursday, October 19th will be used as
part of your score for this assignment. Additional grading tests will then be run against your
highest scoring submission, to determine the rest of your assignment grade.
5
6
7
8
9
10
11
12
list.add(string.charAt(i));
System.out.println("BEFORE: " + list);
// (failing) to remove all 'p's from list
for(int i=0;i<list.size();i++)
if(list.get(i).equals('p'))
list.remove(i);
System.out.println("AFTER: " + list);