Abstractions in computer science hide details to manage complexity. Procedural abstraction mean breaking a program into pieces, implementing each piece as a procedure (also known as a method or a function), and combining calls to the procedures to form the overall program.

Procedural abstraction makes programs easier to write because:

Commonly used abstractions include:

Example: Statistics from test scores

Consider the problem of accepting test scores from a user then calculating and displaying statistics about the scores. Statistics include the mean and the standard deviation.

Score (0-100): 50
Score (0-100 or -1 when done): 75
Score (0-100 or -1 when done): 100
Score (0-100 or -1 when done): -1

Mean   : 75
Std Dev: 20.41

The above shows a sample run of the program. The values 50, 75, 100, and -1 (a sentinel) are input by the user. Everything else shown is displayed by the program.

getTestScores method

public static ArrayList<Integer> getTestScores()
{
    final int MIN_SCORE = 0;
    final int MAX_SCORE = 100;
    final int INVALID_VALUE = -1;
    
    Scanner fromKeyboard = new Scanner(System.in);
    
    System.out.print("Score (" + MIN_SCORE + "-" + MAX_SCORE + "): ");
    int score = asInt(fromKeyboard.nextLine(), INVALID_VALUE);
    
    while(score < MIN_SCORE || score > MAX_SCORE)
    {
        System.out.println("Invalid score");
        System.out.print("\nScore (" + MIN_SCORE + "-" + MAX_SCORE + "): ");
        score = asInt(fromKeyboard.nextLine(), INVALID_VALUE);
    }
    
    ArrayList<Integer> scores = new ArrayList<Integer>();
    
    while(score >= MIN_SCORE && score <= MAX_SCORE)
    {
        scores.add(score);
        
        System.out.print("Score (" + MIN_SCORE + "-" + MAX_SCORE + " or -1 when done): ");
        score = asInt(fromKeyboard.nextLine(), INVALID_VALUE);
    }
    
    fromKeyboard.close();
    
    return scores;
}

Accepting all and only valid test scores from the user is more complicated than it may at first appear. Invalid scores must be rejected. At least 1 valid score must be accepted. The user must be able to indicate when they are done entering scores.

Each significant task in a program should be written as a procedure (often called a method or a function). The procedure should be testable before more of the program is written.

The getTestScores method handles all of the requirements and returns a list of valid test scores entered by the user. The method does not calculate or display statistics about that scores. Calculating statistics about the scores should be done in separate methods. Displaying the calculated statistics should be done in a separate method.

Testing getTestScores

public static void main(String[] args)
{
    System.out.println(getTestScores());
}

The getTestScores method can be tested by calling the method and printing the returned list.

If the user enters: 50, 75, 100, and -1
the test code prints: [50, 75, 100]

getTestScores as an abstraction

Once the getTestScores method has been tested, and any errors fixed, it becomes an abstraction. Whenever we need to accept test scores from the user, we can call the getTestScores method without worrying about how the code inside works.

Many development environments support collapsing methods so that only the method header is visible (and the code inside the method is hidden). This is a visual represntation of abstraction.

getMean method

public static double getMean(ArrayList<Integer> scores)
{
    int sum = 0;
    
    for(int score : scores)
        sum += score;
    
    return sum / (double) scores.size();
}

The getMean method accepts scores, a list of test scores, as a parameter. The method calculates and returns the mean of the scores.

The getMean method does not call the getTestScores method. Combining calls to the methods to form the overall program is handled later.

Testing getMean

public static void main(String[] args)
{
    ArrayList<Integer> scores = new ArrayList<Integer>();
    scores.add(50);
    scores.add(75);
    scores.add(100);
    
    System.out.println(getMean(scores)); // 75.0
}

The getMean method can be tested by

The expected value (75.0) is shown as a comment.

The test code does not require user input. This allows the test to be quickly repeated if an error is detected and corrected.

Additional test cases should be used to determine that getMean works correctly; however, that is beyond the scope of this page.

getMean as an abstraction

Once the getMean method has been determined to work, it can be called without worrying about how the code inside works. This will be useful when calculating the standard deviation.

getStandardDeviation() method

public static double getStandardDeviation(ArrayList<Integer> scores)
{
    final double mean = getMean(scores);
    
    double sumOfSquares = 0;
    
    for(int score : scores)
        sumOfSquares += Math.pow(score - mean, 2);
    
    return Math.sqrt(sumOfSquares / scores.size());
}

Calculating the standard deviation requires calculating the mean. The getMean method is called to calculate the mean. When calling getMean within getStandardDeviation, it is not necessary to remember how the code inside getMean works. This is abstraction (hiding details that are not relevant right now).

Testing getStandardDeviation()

public static void main(String[] args)
{
    ArrayList<Integer> scores = new ArrayList<Integer>();
    scores.add(50);
    scores.add(75);
    scores.add(100);

    System.out.println(getStandardDeviation(scores)); // 20.412
}

The getStandardDeviation method is tested using the same approach as the getMean test.

main() method

public static void main(String[] args)
{
    ArrayList<Integer> scores = getTestScores();
    
    DecimalFormat formatter = new DecimalFormat("#.##");
    System.out.println();
    System.out.println("Mean   : " + formatter.format(getMean(scores)));
    System.out.println("Std Dev: " + formatter.format(getStandardDeviation(scores)));
}

The main method is responsible for the overall program. It calls the other methods and handles the return values.

The main method here also uses a DecimalFormat object to format the output. In this case, the numbers are rounded to 2 decimal places.

While writing the main method, it is not necessary to worry about how the code inside each other method works. All that is necessary is understanding how each method is called and what each method does (not how it does it).

Complete code

ScoreStats.java

The main method in the above file includes the test code commented out.

The file also includes the asInt method. asInt was called within getTestScores but not otherwise shown. Input validation as String discusses asInt in detail.

Help & comments

Get help from AP CS Tutor Brandon Horn

Comment on Procedural abstraction