java-foundations Lesson 11 18 min read

Method Design Patterns

Decomposition, overloading, scope, and Javadoc

Reading: Reges & Stepp: Ch. 3 §3.1–3.2

After this lesson, you will be able to:

  • Apply top-down design to break a problem into focused methods
  • Use stub methods to build a program skeleton before filling in details
  • Write overloaded methods with different parameter signatures
  • Explain how scope isolates variables across methods and blocks
  • Write Javadoc comments with @param and @return tags

The 200-Line main Problem

A student writes a GPA calculator. It reads student data, validates every input, computes the GPA, determines the letter grade, and prints formatted results — all in main. The result: 200 lines of tangled code. When a bug appears in the GPA formula, they have to search through validation logic, input handling, and output formatting to find it.

Methods solve this. Lesson 1.5 introduced method syntax. This lesson teaches you when and how to create methods — the design patterns that separate clean programs from messy ones.

From CSCD 110: In Python, you may have written long scripts that ran top-to-bottom. Functions helped, but Python did not force you to use them. Java’s method-based design is more deliberate — and the discipline of breaking programs into small, named, testable pieces scales from CSCD 210 all the way to professional software.


Top-Down Design: Big Picture First

Top-down design starts with the high-level steps, then breaks each into a method:

Level 1 — What does the program do?

  1. Read student data
  2. Calculate GPA
  3. Display results

Level 2 — Translate to methods:

public static void main(String[] args) {
    String name = readName();
    int numCourses = readCourseCount();
    double gpa = calculateGPA(numCourses);
    displayResults(name, gpa);
}

Reading main tells you the program’s story at a glance. The details are hidden inside well-named methods. This separation of what from how makes programs dramatically easier to understand, modify, and debug.

Key Insight: Top-down design lets main tell a story. Each line is a step in the narrative. If you can describe the program in 4–6 English sentences, each sentence should become a method call in main.


Stub Methods: Build the Skeleton First

When designing top-down, start with stub methods — methods with the correct signature but placeholder implementations:

public static String readName() {
    return "TODO";
}

public static int readCourseCount() {
    return 0;
}

public static double calculateGPA(final int numCourses) {
    return 0.0;
}

public static void displayResults(final String name, final double gpa) {
    // stub: no implementation yet
}

This program compiles and runs (doing nothing useful). You can now implement one method at a time, testing each as you go. If something breaks, you know exactly which method you just changed.

The Trick: Start with stubs, then fill in one method at a time. Your program always compiles. You test incrementally. Bugs are isolated to the method you just changed.


Method Design Guidelines

1. One Clear Purpose

A method should do one thing and do it well. Test: can you describe it in one sentence without using “and”?

Good “Calculate the area of a circle given the radius.”
Bad “Read the user’s age AND validate it AND calculate the birth year.”

2. Return Values Over Side Effects

If a method computes something, return it. Don’t force printing inside computation methods:

// Bad: computes a value but only prints it
public static void calculateTotal(final int a, final int b) {
    int total = a + b;
    System.out.println(total);  // caller can't use the value
}

// Good: returns the value, caller decides what to do
public static int calculateTotal(final int a, final int b) {
    return a + b;
}

3. Descriptive Names

Method names should be verb phrases: calculateMonthlyInterest() not processData(). Parameter names should be meaningful: radius not x.

4. Reasonable Parameter Count

More than 3–4 parameters usually means the method is doing too much. In Week 8, you will learn about objects for grouping related data.

Check Your Understanding

Which is the better method design?


Method Overloading

Overloading lets you define multiple methods with the same name but different parameter lists:

public static int max(final int a, final int b) {
    return (a > b) ? a : b;
}

public static double max(final double a, final double b) {
    return (a > b) ? a : b;
}

public static int max(final int a, final int b, final int c) {
    return max(max(a, b), c);  // calls the two-argument version
}

Java picks the right version based on the arguments:

max(5, 3);       // calls max(int, int) → 5
max(5.5, 3.2);   // calls max(double, double) → 5.5
max(1, 2, 3);    // calls max(int, int, int) → 3

The signature is the method name plus parameter types. You can overload by changing the number, types, or order of parameters — but not by changing only the return type.

Common Pitfall: You cannot overload by return type alone. int getValue() and double getValue() cannot coexist — Java cannot tell which one to call when you write getValue().

Check Your Understanding

Which pair of methods is a valid overload?


Scope: Variables Stay in Their Lane

A local variable exists from its declaration to the closing } of its block. Each method is an isolated box:

public static void task1() {
    int count = 0;
    for (int i = 0; i < 10; i++) {
        count++;
    }
    System.out.println("Task 1: " + count);  // 10
}

public static void task2() {
    int count = 0;  // completely separate from task1's count
    for (int i = 0; i < 5; i++) {
        count++;
    }
    System.out.println("Task 2: " + count);  // 5
}

Both methods have a variable named count, but they are entirely independent — different stack frames, different memory locations.

Block scope applies to if, for, and while bodies too:

for (int i = 0; i < 10; i++) {
    int temp = i * 2;  // temp exists only inside this loop
}
System.out.println(temp);  // COMPILE ERROR: temp is out of scope

Parameters follow the same rule — they are local variables that exist only inside the method.


Javadoc: Professional Documentation

Javadoc comments use /** ... */ syntax with @param and @return tags:

/**
 * Determines whether a year is a leap year.
 *
 * A leap year is divisible by 4, except for century years,
 * which must be divisible by 400.
 *
 * @param year the year to check (e.g., 2024)
 * @return true if the year is a leap year, false otherwise
 */
public static boolean isLeapYear(final int year) {
    return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
}

Java’s tooling can generate HTML documentation from these comments automatically. This is the professional standard for all Java code.


Complete Example: GPA Calculator (Decomposed)

Here is the GPA calculator redesigned with top-down decomposition, overloading, and Javadoc:

import java.util.Scanner;

public class GPACalculator {
    private static final Scanner SCANNER = new Scanner(System.in);

    /**
     * Reads a validated integer from the user within a given range.
     *
     * @param prompt the message to display
     * @param min the minimum acceptable value
     * @param max the maximum acceptable value
     * @return a validated integer in [min, max]
     */
    public static int readInt(final String prompt, final int min, final int max) {
        int value;
        do {
            System.out.print(prompt);
            value = SCANNER.nextInt();
            if (value < min || value > max) {
                System.out.println("Must be " + min + "-" + max + ". Try again.");
            }
        } while (value < min || value > max);
        return value;
    }

    /**
     * Calculates GPA from total grade points and credit hours.
     *
     * @param gradePoints total grade points earned
     * @param creditHours total credit hours attempted
     * @return the GPA as a double
     */
    public static double calculateGPA(final double gradePoints,
                                       final int creditHours) {
        return gradePoints / creditHours;
    }

    /**
     * Converts a numeric GPA to a letter grade.
     *
     * @param gpa the GPA value (0.0-4.0)
     * @return the letter grade as a String
     */
    public static String toLetterGrade(final double gpa) {
        if (gpa >= 3.5) { return "A"; }
        if (gpa >= 2.5) { return "B"; }
        if (gpa >= 1.5) { return "C"; }
        if (gpa >= 0.5) { return "D"; }
        return "F";
    }

    public static void main(String[] args) {
        int courses = readInt("Number of courses (1-10): ", 1, 10);

        double totalPoints = 0;
        int totalCredits = 0;

        for (int i = 1; i <= courses; i++) {
            System.out.println("\n--- Course " + i + " ---");
            int credits = readInt("Credit hours (1-5): ", 1, 5);
            int grade = readInt("Grade points (0-4): ", 0, 4);
            totalPoints += grade * credits;
            totalCredits += credits;
        }

        double gpa = calculateGPA(totalPoints, totalCredits);
        String letter = toLetterGrade(gpa);

        System.out.printf("%nGPA: %.2f (%s)%n", gpa, letter);
    }
}

Notice how main reads like a story: read course count, loop through courses collecting data, calculate GPA, display results. Each helper method is focused, testable, and documented.

Check Your Understanding

In the GPA calculator above, can the main method directly access the value variable inside readInt?


Summary

Top-down design means planning the big picture first, then breaking it into small, focused methods. main should read like a story — each line is a method call that describes one step.

Stub methods let you build the program skeleton first, then fill in one method at a time. Your program always compiles, and bugs are isolated.

Good methods have one clear purpose, descriptive names, and return computed values instead of printing them. Overloading lets you reuse intuitive names with different parameter types.

Scope isolates variables: local variables and parameters exist only inside their method. Block scope applies to if, for, and while bodies too. Javadoc documents methods professionally with @param and @return tags.

Next lesson: Midterm review and synthesis — connecting all Weeks 1–5 concepts with call-stack tracing, combined patterns, and exam preparation strategies.