APS101-Winter 2008, Assignment 1

Introduction

The purpose of this assignment is to give you practice in a number of areas:

The classes that you write in this assignment don't "do" much - they are meant to represent a very simplified portion of the world (planes, runways and airport) according to the specifications below. Most of the "doing" is done by your test case classes: they test the methods of your other classes. The test case classes create objects of your other class types and invoke their methods to prove that the methods work properly.

The main purpose of the assignment is to get you to think about classes, objects, class (static) methods and variables, and object (instance) methods and variables, and to write them all out, along with the tests, to give you lots of practice in writing Java. Understanding the concepts is not difficult; one of the challenges is to translate the English description you are given below into Java in the first place. The other main challenge is getting everything right in Java - especially if you've never done it before. What the classes are all about is sometimes artificial and does not correspond to the real world - that's because this is an exercise, and some things are simplified to keep them doable.

To help you do the exercise right, this description goes into great detail. It will require several readings. Be sure to read ALL sections: The Task, Restrictions, What to Hand in, Marking, and the Hints section.

Overview

The Task

For this assignment, you are asked to work with 6 classes: Plane, Runway, Airport, PlaneTester, RunwayTester and AirportTester. As you can see by their names, the last three exist in order to test the first three.

An exact specification is given below for three of the classes: Plane, Runway and Airport. That is, there is a description, in English, for every variable and method that these classes are to have.

For the other three classes: PlaneTester, RunwayTester and AirportTester, we give you general guidelines. We don't provide exact instructions, but you can read here about what to do with testing classes. There are also labs and lectures that deal with testing and you should follow the examples given there.

To help you start with the assignment, we provide you with the code for Runway class and RunwayTester. So, you will produce and submit the rest of 4 classes. However, we strongly recommend not to look at the given code at the first place. Instead, try to write them yourself (even a feeble attempt is way better than doing nothing and looking at the code). Then, compare what you have written with the given code to see what's missing or needs improvment. After this, try to write the other 4 classes.

Background

Consider an airport, with runways and planes. This assignment is about representing the planes and runways as objects with different properties described below, and simulating some of the operations of an airport. Planes have destinations or origins, take-off or landing schedules, and flight numbers. Planes are parked on runways. Planes taxi from one runway to another. Runways have directions, and they have planes parked on them.

An airport is a busy place. To keep things simple we are only going to keep track of one plane waiting to land and the planes on the runways (on the ground). Once a plane takes off or heads to the terminal it is no longer the program's concern.

Most airports have four runways that intersect approximately at right angles, two north-south and two east-west. Chicago's Midway Airport is a good example. While waiting to take off, planes taxi around the airfield from runway to runway. In our case, we will assume that they always taxi in a clockwise direction, that a runway holds only one plane at a time, and that it takes 3 minutes to taxi from one runway to the next.

Planes take off into and land into the wind. So one runway is designated as the active runway for take-off and landing. When a plane takes off or lands it creates turbulence. It takes time for the turbulence to dissipate and it is safe for the next plane to land or take off. In our case, we will assume that it takes a minimum of 8 minutes.

Here are the class descriptions:

Pay close attention to the tables below! There are two crucial things to remember:

Class Plane

The Plane class represents a plane. It has a number of fields: the flight number, whether it is landing or taking off, the expected time of take off or landing, and the destination or origin of the plane.

Here are the definitions of the fields and methods of the Plane class:

Variables

Type

Description

String

The flight number of this plane.

boolean

Whether the plane is landing or taking-off. true indicates landing and false indicates taking-off.

int

The expected time of the landing or take-off.

String

The location where the plane is taking-off to or landing from (i.e., the destination or origin of the plane).

int

A static variable. Keeps track of the total number of planes that have been created.

Methods

Constructor

Description

Plane(String, boolean, String, int)

Constructor for the Plane class. The parameters, in order, are: the flight number, whether the flight is landing or taking-off, the destination or origin of the flight, and the expected time of landing or take-off.

Method Name

Description

getFlightNumber()

Return the flight number of this plane (as a String).

getLocation()

Return the destination or origin of this plane (as a String).

isLanding()

Return true if this plane is landing, and return false if this plan is taking off (as a boolean).

setScheduledTime(int)

Set the time (in minutes) when this plane is expected to land or take-off.

getScheduledTime()

Return the time (in minutes) when this plane is expected to land or take-off (as an int).

getTotalPlanes()

A static method. Returns the total number of planes that have been created.

Class Runway

The Runway class represents a runway at an airport. The runway stores the direction of traffic on the runway, whether the runway is occupied, and the plane that is currently on the runway.

The Runway class has the following variables and methods:

Variables

Type

Description

String

The direction of traffic on this runway (either East, West, North or South).

boolean

Represents whether this runway is occupied by a plane: true indicates that there is a plane on the runway, and false indicates that there are no planes on the runway. A runway will either have 0 or 1 planes on it.

Plane

The plane currently on this runway (or null if there is no plane on the runway).

Methods

Runway(String)

Constructor for the Runway class that creates a runway with no plane on it. This method takes one parameter, which is the direction of traffic (East, West, North, or South).

Runway(String, Plane)

Constructor for the Runway class. Takes two parameters: the direction of traffic (East, West, North, or South) and a Plane object, which is the plane currently on this runway. The runway is occupied.

Method

Description

getPlane()

Return the plane on this runway (as a Plane).

setPlane(Plane)

Set the plane on this runway to the given Plane. The runway is occupied.

isOccupied()

Return true if this runway is occupied, and false otherwise.

notOccupied()

Set this runway to not occupied (if there is plane on the runway, it should be removed).

getDirection()

Return the direction of traffic on this runway.

hasSameDirection(Runway)

Return true if this runway has the same direction as the given runway, and false otherwise.

Class Airport

This class represents an airport. It has a name, a city, four runways, a current time and several methods for setting and reading the values of these variables.

The Airport class has the following variables and methods:

Variables

Type

Description

String

The name of the airport.

String

The name of the city in which the airport is located.

Runway

The eastbound runway.

Runway

The westbound runway.

Runway

The northbound runway.

Runway

The southbound runway.

Runway

The active runway for take-off and landing (one of the four runways above.)

int

The current time (measured in minutes).

int

The time when the active runway was last used (i.e., when a plane last took-off from or landed on the active runway) (measured in minutes).

Plane

A plane that is in the air waiting to land (or null if no plane is waiting to land).

Constants

Type

Name

Value

Description

public static final int

WAIT_TIME

8

The safety interval between take off or landing planes (measured in minutes.) That is, the wait time between take offs and landings.

public static final int

TAXI_TIME

3

The time it takes for a plane to taxi from one runway to the next (also this is the time it takes to taxi from the terminal to a runway, or from a runway to the terminal).

public static final int

LANDING_TIME

5

The time it takes for a plane to land.

public static final int

TAKE_OFF_TIME

5

The time it takes for a plane to take-off.

Methods

Constructor

Description

Airport( String, String, int, Runway, Runway, Runway, Runway, Runway )

Constructor for the Airport class. There are eight parameters, in this order: the name of the airport, the city the airport is in, the current time (in minutes), the east runway, the west runway, the north runway, the south runway, and the active runway (the runway presently being used for take-off and landing).

Method

Description

getName()

Return the name of this Airport (as a String).

getCity()

Return the name of the city the airport is in (as a String).

getEastboundRunway()

Return the eastbound runway (as a Runway).

getWestboundRunway()

Return the westbound runway (as a Runway).

getNorthboundRunway()

Return the northbound runway (as a Runway).

getSouthboundRunway()

Return the southbound runway (as a Runway).

getActiveRunway()

Return the active runway (the runway currently being used for take-offs and landings) (as a Runway).

getTime()

Return the current time (as an int).

getTimeLastUsed()

Return the time when the active runway was last used or 0 if the runway has never been used (as an int).

getLandingPlane()

Return the plane that is scheduled to land on the active runway or null if there is no plane waiting to land (as a Plane).

taxiAllPlanes()

Taxi each of the planes from one runway to the next clockwise (North to East, East to South, South to West, West to North). Adjust the current time to reflect the time that has passed (all planes are taxied simultaneously, so the elapsed time is the time to taxi one plane).

setActiveRunway(Runway)

Set the active runway (the runway currently being used for take-off and landing) to the given Runway.

taxiToRunway(Runway, Plane)

Taxi a plane from the terminal to the given runway. The parameters are: the runway and the plane. (You may assume that the given runway is not occupied and the plane is at the terminal; we won't keep track.)

taxiToTerminal(Runway)

Taxi a plane from a runway to the terminal (You may assume that there is a plane on that runway.) Update the status of runway.

setLandingPlane(Plane)

Set the given plan to be waiting to land at this airport.

canLand()

Returns a boolean indicating whether the circling plane can land. There are two reasons why the plane would not be able to land:

  • the active runway is occupied by another plane
  • not enough time has passed since the active runway was last used

canTakeOff()

Returns whether the plane on the active take-off and landing runway can take-off. There are two reasons why the plane would not be able to take-off:

  • there is no plane on the active runway
  • not enough time has passed since the active runway was last used

land()

Moves the waiting incoming plane to the take off/landing runway. This involves:

  • updating the current time to reflect the time it takes for the plane to land
  • updating the state of the runway

You may assume that this method will only be called when the conditions described in canLand() are met.

takeOff()

The plane currently on the active take-off and landing runway takes-off. This involves:

  • updating the current time to reflect the time it takes for the plane to take-off
  • updating the state of the runway

You may assume that this method will only be called when the conditions described in canTakeOff() are met.

allOccupied()

Return true if all four runways are occupied, and false otherwise.

partiallyOccupied()

Return true if at least one runway is occupied but not all runways are occupied, and return false otherwise.

Note:

As a general rule, you can assume that all parameters have correct values, not null where not null are expected, positive where expected, etc. This means that your methods don't have to worry about these input values and your test classes don't have to test with them (we won't).

Test Classes: RunwayTester, PlaneTester and AirportTester

How do you know whether the Airport, Runway and Plane classes you're designing are correct or not? One way is if you test them, to see if they do what they are supposed to do:

For each method that you add to Plane, Runway and Airport, you are expected to write one or more test methods in PlaneTester, RunwayTester and AirportTester classes, respectively. In these methods, you usually create at least one new object and test your method on it.

The Test... classes are the JUnit test suites you'll design, which perform these testing tasks for you. Lectures and tutorials will introduce you to JUnit - it comes with DrJava. You can also get more detail by following the links on the course main website's Java links page.

Here are a couple of sample tests for the classes; they should give you an illustration of what your Test... classes should do. Also, we have provided you with RunwayTester but as we mentioned above do not look at it at the first place, instead try to write it yourself and then compare your testcases with the given code.

Make sure that your test classes adhere to the following principles:

Remember that if you change static variables in an early test, they will retain their values in later tests.

Important: the tests in a JUnit test suite are not necessarily run in the order in which you list them in your suite. So when testing static variables, record their initial value at the beginning of the test, and test that the change in the value is what you expect.

Restrictions

You must not use if statements, loops and arrays. If you do, you will lose marks. (We leave those for later assignments.)

You will find that you can do everything you need to do with the arithmetic operators (+, -) and boolean operators (<, <=, ==, =>, >, !=, &&, ||, !).

Marking

What is important? Several things. We do not hand out a detailed marking scheme, but here are the general outlines, and some explanatory notes:

What to Hand In

The course website describes how you hand in your assignment. This section tells you what to hand in:

Do NOT hand in Runway.java and RunwayTester.java (as we have already provided you with their solutions!).

Remember that spelling, including case, count in Java: your files must be named exactly as above.

NOTE: Other than the signoff statement, only hand in the files that end with the .java suffix. Be careful about this, because in the same place as your .java files you may also have files with the extension .class (that is, they end with the .class suffix), but otherwise have the same name. Two particular pitfalls:

Since .class files cannot be read by TAs or run with our testing programs, submitting the wrong files might cause you to fail the assignment. Every year, a half-dozen students submit the wrong file; we simply cannot do anything to fix this mistake.

Hints

How you should proceed

This is a large and complex assignment (for those who've never done anything like this before). It pulls together a number of things covered in lectures and labs: that is a lot of stuff. So here are a couple of pointers on how to go about this.

Read the assignment several times. When you think you have an idea of what you are asked to do, start defining your first class. This should be the Runway class so you can compare your attempts with the solution provided. Write the class comment, then the class header with an empty body. Compile. Write the variables and add the comment that shows you know what each one is for. If you can't write the comment, you may be having trouble understanding the purpose of the variable. Compile.

Now write a header and empty body for the test class that tests the class. Compile.

Go back to the original class. Pick a simple method. Write the comment for the method and the method header, with an empty body.

If you understand what the method is supposed to do, then you should be able to write at least one test for the method in the test class, before you actually write the method.

Go back to the test class.

Write the test method. Usually this means creating an object of the class being tested, then calling the method being tested, and using one of the assert methods to show that the method did the right thing. (You may not be able to compile the test method until after you write and compile the original method you are testing, which is the next step.)

Write the original method body. Compile the class. Compile the test class.

Run the JUnit test. Fix any problems, re-run until the test passes. You may find that sometimes the problem is with the test method: make sure you're testing what you think you are testing.

You now have the skeleton of a class, and its test class!

Repeat with more methods: write the comments and the method header, write the test(s), then write the original method, compile and run the test. Fix the problems and move on. JUnit makes sure that the tests you wrote earlier are also performed, so you can see if you inadvertently introduced errors in methods you wrote earlier.

Keep working, back and forth, between a class and its test class. This is a useful approach: it makes sure that your classes work by testing them at every step, your tests are easier to define, because you have the method being tested fresh in your mind, and when you are done, both classes are written.

At first, this will seem very time-consuming. The good news is that it will get easier and faster the more you do it. Really. The only way to get better at it is to keep doing it.

NullPointerException Notes (mainly for those interested)

If you're getting NullPointerException reports and would like to learn more about them, here is some background. (We will not take marks off for these exceptions and reading this section is optional.)

NullpointerExceptions happen when Java is attempting to excute an instruction which makes a reference to an object's method or variable when that object's value is null (the object does not exist). The trick is to make sure to make the reference only if the object exists.

The simplest way to do this would be with an if statement - but we have not covered that yet; and besides, you are not allowed to use if in this assignment.

Hmmmm.

What we want to do is to prevent Java from calling a method or evaluating a variable just in case the owner of that method or variable happens to be null. This is where you can be clever and use the && operator:

When Java is trying to evaluate a boolean expression with this operator, it starts by evaluating the expression on the left side of the operator first. If it finds that the left side is false, it will not bother to evaluate the right side. For example, given

(a == b) && (c < d)

it will first check if a is equal to b. If it is not, then this part is false, and there is no point in seeing whether c is less than d: the whole expression will be false anyway.

So - without giving too much away - you can prevent Java from, for example, calling a method on a null object if you place this method call as the second part of a && expression. The first part of the expression must be such that it is false if the object is null: then the second part will never be executed.