How to Design Class Hierarchies: Lecture Notes

Viera K. Proulx and Matthias Felleisen

January 12, 2003

Week 1 - Lecture 2: Ask Me a Question

0.0.1  Example of the use of design recipe

We are planning a trip from Boston to New York, driving at the average speed of about 50 miles per hour. We would like to know how long will the trip take. The map shows that the distance is 200 miles. It looks silly to design a program for just this one problem, but we hope to reuse this program to compute the duration of many other trips in the future.

  1. Data Analysis and Data Definition
    The information given to us is the distance (a number) and the average speed (a number). The function produces as a result another number which represents the expected duration of the trip.
    In Java general numbers that represent quantity that may not always be a whole number are called double. So, the data this program consumes is:
    double distance
    double speed
    It also produces a double.

  2. Purpose and Header
    The purpose statement states briefly and clearly what is the goal of this program. The header of the function is a Java language description of the data arguments the function will consume and the type of result it will produce. The header also gives the function a name. The following is the purpose and header for our function:

        /* Purpose: compute the travel time
        given the distance and average speed */
        double travelTime(double distance, double speed)
    

    The function name is travelTime, is consumes two arguments: distance and speed, the type of each argument is double, and it produces a result of the type double.

  3. Examples
    Our first example is the trip to New York City. The distance is 200 miles, the average speed is 50 mph. We expect the trip to take 200/50 hours, or 4 hours. The trip to Washington, DC which is 420 miles away will take about 7 hours if we average 60 mph. We formulate the examples as Java test statements as follows:

        expected(4.0);
        actual( travelTime(200.0, 50.0) );
    
        expected(7.0);
        actual(  travelTime(420.0, 60.0) );
    

    The first statement will display the expected value in the console window, labelled as 'Actual:'. The second statement will invoke the function where the specified values will be 'plugged in' for the corresponding arguments.
    The examples help us determine the nature of computation that is to be done. In this case it is a simple division of the distance value by the value of average speed.

  4. Body
    The body of the function is simple:

        return(distance/speed);
    

    If the body of the function begins to look complicated, we need to think about subdividing the problem into simpler subproblems and delegate the computation of the subproblem results to other functions. The rule is one task - one function. For example, if we planned a trip to Chicago, the total distance would be the sum of several segments of the trip. The computation of the total distance would then be a separate task delegated to another function.

    The complete function looks as follows:

        /* Purpose: compute the travel time
        given the distance and average speed */
        double travelTime(double distance, double speed){
            return(distance/speed);
        }
    

  5. Tests
    The statements that specify the examples and tests are placed in a special Java test file. The complete test file FunctionTest.java would look like this:

    /* FunctionTest.java 1.0  1 January 2003 */
    
    public class FunctionTest extends JPFalt {
        public static void main(String[] args) { new FunctionTest(); }
    
        ////////////////////////////////////////////////////////////////
        // Place your actual methods here.                            //
        ////////////////////////////////////////////////////////////////
    
        /* compute the travel time given the distance and average speed
         -------------------------------------------------------------*/
        double travelTime(double distance, double speed){
            return(distance/speed);
        }
    
        /* Examples/Tests:
         --------------------------------------------------------------*/
        void travelTimeTest(){
    
            testHeader("travelTime(double distance, double speed)");
    
            expected(4.0);
            actual( travelTime(200.0, 50.0) );
    
            expected(7.0);
            actual(  travelTime(420.0, 60.0) );
        }
    }
    

0.0.2  Other kinds of data

The answer to the question whether there is an apple in a fruit basket is either 'yes' or 'no', or alternatively true or false. Java uses boolean data type to represent true or false values. The data type that represents the number of pieces of fruit in the basket is int (standing for integer, or whole number). Name of a person or city is represented as a String, for example "New York City".

A function that determines whether a person is old enough to vote illustrates the use of boolean values and introduces relational operators. The Java expression age > 18 produces a boolean value true or false. Additional relational operators are <, <=, >=, ==, !=.

The following is an example of a function that returns a boolean value:

    /* determine whether a person can vote, given the year of birth */
    boolean canVote(int dob){
        return(2003 - dob) > 18;
    }

    /* Examples/Tests:
     --------------------------------------------------------------*/
    void canVoteTest(){
        testHeader("canVote(int age)");

        expected(false);
        actual( canVote(1985) );

        expected(true);
        actual(  canVote(1982) );

        expected(false);
        actual( canVote(1995) );
    }

0.0.3  Composition of functions

Sometimes the task is complex and hard to understand. Careful analysis typically reveals that the problem can be subdivided into smaller, simpler tasks.

You want to estimate the cost of gas needed for a trip to New York City. The distance is 210 miles, the cost of gas is $1.50 per gallon and the car averages 30 miles per gallon. The data analysis is simple. The function consumes distance (double miles), price per gallon (double price), and the gas consumption (double mpg). The purpose and the header follow:

    /* compute the price of gas needed for a trip,
       given the distance, the cost of gas, and miles per gallon
      -----------------------------------------------------------*/
    double tripCost(double distance, double mpg, double price)

Here are some examples. On the trip to New York City the car will need 210/30 = 7 gallons of gas, and so the cost of the trip will be 7 * $1.50 = $10.50. Another trip is to Washington, DC which is 400 miles away. The car consumes 20 mpg and the gas price is $1.60. The car will need 400/20 = 20 gallons of gas at a cost 20 * $1.60 = $32.00. In Java, the examples are:

    expected( 10.50);
    actual  ( tripCost(210, 30, 1.50) );

    expected( 32.00);
    actual  ( tripCost(400, 20, 1.60) );

The body of the function follows:

        return (gallonsNeeded(distance, mpg) * price);

The call of the function gallonsNeeded recognizes the fact that computing the number of gallons is a separate task. The function has not yet been defined, but our expected use guides its definition. The development of the tripCost function is nearly complete, but we cannot run the tests until the helper function gallonsNeeded is available.

Only examples and tests are needed to develop the helper function gallonsNeeded - the rest follows from the work done on tripCost function and the use of the gallonsNeeded function in its body. Examples and test are derived directly from examples and tests for the tripCost function. The final version is:

    /* compute gallons of gas are needed for a trip,
       given the distance, and miles per gallon
    -------------------------------------------------------------*/
    double gallonsNeeded(double distance, double mpg){
        return distance / mpg;
    }

    /* Examples/Tests:
     --------------------------------------------------------------*/
    void gallonsNeededTests(){
        testHeader("gallonsNeeded(int age)");

        expected( 7.0);
        actual  ( gallonsNeeded(210, 30) );

        expected( 20.00);
        actual  ( gallonsNeeded(400, 20) );
    }

Last modified: Sun, Jan 12, 2003, 8:45 pm
HTML conversion by TeX2page 4q4