CIS 211 - Computer Science II
Winter, 2003 - A. Hornof

Project 3

This Project is due on Monday, February 3, at 5 PM.

Purpose

This project will give you practice the following basic OOP techniques:

  • Working with a class hierarchy that uses that uses abstract methods to achieve polymorphic behavior.
  • Using abstract base classes to specify the interface to derived classes.
  • Observing the order of construction in a class hierarchy.
  • Organizing your code with packages.

Overview

In this project, you will create an object-based "model" of the maritime simulation world. The model will have Ships and Islands, both of which are direct subclasses of SimObject (which is a direct subclass of Object). There will be three classes that inherit from Ship. They are CruiseShip, Tanker, and PirateShip. You will use a simple P3.java file that we provide to create a series of objects, and also to add them all to the one MaritimeModel object that gets created. It will keep track the state of the "world" which, for this project, is just the objects you create. It will maintain three ArrayLists, one of all SimObjects, one of all Islands, and one of all Ships.

As each object is created, all of the constructors that are called will announce the object's creation with a message to the standard output. Once all objects are created, the program will then display the contents of the three lists in the model.

All of the classes you create will be part of the maritime package, which will be imported by P3.java.

In subsequent projects, you will load the model from the data input file from Projects 1 and 2, and you will also build a corresponding "view" and "controller" for the model.

An Evolving Program and Specification

The Project 3 Addendum posted on the course web site is officially part of these project specifications. You should check it at least at the beginning of each work session. Be sure to start the project early enough to have an opportunity to ask for clarification in case it is necessary.

Advice on How to Do It

Start early. It's not a lot of code, but it has to be organized carefully. Read the specification a couple times. Start small, creating the class hierarchy incrementally, continually making sure that your code compiles and behaves as you expect. Create a P3.java early on, calling constructors with some "hard-coded" arguments (written directly into the source code) so that you can verify things are working as you think they are. One by one, write and add each new class in a separate file. Create the Location class first because the others will need it. Then you can start from the bottom of the hierarchy, building the individual ship classes first, or at the top, building the SimObject first. You may need to create some interim constructors and methods to make sure everything is working, but remove them before you turn it in (or put them at the bottom of your classes and comment them out). Perhaps, just to get started, set SimObject to be a concrete (non-abstract) class. But be sure to eventually set it as "abstract" and immediately recompile after you do; you will probably find some cleaning up to do.

At some point when things are working, get the package designation working. The simplest way is to make sure your current directory (".") is in your CLASSPATH, and all of your model files (SimObject.java, Ship, ...) are in a subdirectory (named "maritime") off of your current directory. Test your package by seeing if you can run it using the P3Test1.class and P3Test2.class files that we have provided for you for minimal testing purposes. P3Test1.class should work before you have built the MaritimeModel class. P3Test2.class should only work after the MaritimeModel class is working. Save the MaritimeModel class until after everything else is working.

Test your code thoroughly by compiling and running it from the command line prompt, ideally from more than one platform (such as both Windows and Unix). Verify that you are using Java 1.3.1 or 1.4.0. Test your code by swapping it and running it with the P3Test.class programs and maritime classes available on the course web site. in theory, any combination of your files and our files should run together. If your output does not match the sample output, you probably did not follow the specification accurately. The errors should give you some clues as to what is wrong. If your output does match the sample output, then your code has passed a minimum set of requirements. You still need to conduct your own set tests.

After everything is working, delete all the class files, including in all subdirectories, compile again, and test everything again.

What to Turn In

You will turn in one file for each of the following eight classes. The filename will be the class name plus ".java". The class names must be spelled exactly as listed below. You will also turn in a P3.Java that you write to test your classes. It should call each constructor that can be called, create a number of objects, put them in the MaritimeModel, and then show the contents of all of the lists in the model.

The Classes

The remainder of the assignment specifies how the maritime package will be accessed and how it will function. Specifically, the remainder specifies the public classes and methods that you will implement. All of the following classes should be defined as part of the "maritime" package.

All classes will be public and concrete (non-abstract) except when noted. All methods that are listed here will be public (except as noted in MaritimeModel). You may not add any classes and you may not add any public methods, but you may create whatever nonpublic variables and methods that you like. Of course, follow good programming practice such as by giving your variables and methods meaningful names. Make sure you spell your classes and public methods the same as listed here, and that you use the same return types and parameter types. You can name the actual parameters (such as "name" and "location") whatever you like, though. The names are chosen here for illustration purposes. You might want to change them so that you can more easily use them as instance variables.

Put instance variables as high as possible (that makes sense) in the class hierarchy, and have your constructors call the superclass constructors whenever it is feasible.

SimObject class

This is an abstract class that keeps track of the name and location of the object. It has the following methods:

SimObject (String name, Location location)
Creates a SimObject with the given name and at the specified Location loc.
Prints to the standard output:
SimObject <name> constructed
String toString()
This method will have no body (no curly brackets) and will be defined with the keyword that insures it is defined in a subclass.

Island class

This is a direct subclass of SimObject. It keeps track of how much fuel is currently on the island, and how many units of fuel are generated on the island every hour.

Island(String name, Location location, int fuel, int fuelprod)
Creates an Island object with the given name, at the specified location, with the specified fuel level, and the specified fuel production rate.
Prints to the standard output:
Island <name> constructed
String toString()
Returns:
Island <name> at (<X>, <Y>), fuel <fuel>, production rate <fuelprod>

The (<X>, <Y>) is the location, and the Location class's toString() method will provide the nice formatting for you here.

Ship class

This is an abstract class and is a direct subclass of Ship. It shouldn't need any instance variables.

public Ship (String name, Location location)
Creates a Ship with the given name and at the specified Location location.
Prints to the standard output:
Ship <name> constructed
String toString()
This method will have no body (no curly brackets) and will be defined with the keyword that insures it is defined in a subclass.

CruiseShip class

This is a direct subclass of Ship. It shouldn't need any instance variables.

public Ship (String name, Location location)
Creates a CruiseShip with the given name and at the specified Location location.
Prints to the standard output:
CruiseShip <name> constructed
String toString()
Returns:
CruiseShip <name> at (<X>, <Y>)

Tanker class

This class follows the exact same pattern as CruiseShip. (Replace all occurrences of "CruiseShip" with "Tanker".)

PirateShip class

This class follows the exact same pattern as CruiseShip.

Location class

This is a generic location object that just keeps track of the X and Y coordinates of an SimObject.

public Location (int x, int y)

Creates an integer pair that specify an (x, y) location in the Cartesian coordinates of the maritime world.
String toString()
Returns:
(<X>, <Y>)

MaritimeModel class

This class is not part of any class hierarchy (other than being a direct subclass of Object), though is is in the maritime package.

This class will hold everything. It is listed last because you should implement it last. There are some subtle details in its definition. The other classes will be easier. Get them all working first.

MaritimeModel will maintain three ArrayLists: One of all the SimObjects, one of all the Islands, and one of all the Ships. Since Islands and Ships are also SimObjects, every Ship or Island will be on two lists.

One and only one MaritimeModel will be created each time the program is run. It is a "singleton."

private static MaritimeModel instance;
This variable holds the one instance of the MaritimeModel that will be created. Note that it is a static variable. What does that mean?
private MaritimeModel ()
This is a private constructor, which is unusual. We do this as part of a means of insuring that only one MaritimeModel is created. You must insure that this constructor will be called only once in the entire execution of the program.
Prints to the standard output:
SimObject <name> constructed
static MaritimeModel getInstance()
Returns the one MaritimeModel instance, providing a universal access point to the model throughout the program. Note that it is a static method. What does that mean?
add (Island island)
Adds an Island to the model.
add (Ship ship)
Adds a Ship to the model. Note this opportunity for polymorphism in the add() methods.
describeSimObjects ()
Outputs a blank line to the standard output, followed by
SimObjects in the MaritimeModel:

and then outputs toString() for each of the objects in the list of SimObjects in the model, in the order that they were added to the model.

describeShips ()
Follows the pattern of describeSimObjects ().
describeIslands ()
Follows the pattern of describeSimObjects ().