Skip to content
Ameen Abozaid
PublishedBEG-016-L02

Decomposition

  • Lesson 2 of 6
  • 12 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

Once you understand a problem, the next question is how to start solving it. For anything beyond a one-liner, the answer is decomposition: breaking a big problem into small steps, each of which you can solve on its own.

Think of a long trip. You do not drive "from the city to the coast" in one move. You drive from the city to the first town, then to the next, then onward, and each leg is a short, manageable drive. A big programming problem is the same: a chain of small problems.

Why this matters: beginners often freeze in front of a large task, or write one long block where everything depends on everything else. Decomposition gives you a calm way to begin, and it explains why methods exist.

In the previous lesson, "Understanding the problem first", you wrote a restatement with inputs, outputs, and constraints. Here you use it to produce a list of steps.

Learning Objectives

By the end of this lesson you will be able to:

  • Split a problem into steps, each described in one plain sentence.
  • Check that the steps, in order, turn the inputs into the outputs.
  • Split a step again when it is still too big.
  • Decide which steps should become methods.
  • Join separately solved steps into one working program.

Written Explanation

Big problems are chains of small ones

A problem such as "report the average of the valid grades" looks like one task. Looking closer, it is several small tasks in a row: look at each grade, decide if it is valid, add up the valid ones, count them, divide, and show the result. None of these is hard. The problem felt hard only because you looked at it as a whole.

Decomposition is the habit of asking, "what are the smaller things I must do, in what order?"

A simple method

  1. Start from your restatement. The inputs are what you have at the start; the outputs are what you must have at the end.
  2. Ask what must happen in between. Write the steps as short plain sentences, one action per sentence, starting each with a verb: "Read", "Skip", "Add", "Divide", "Print".
  3. Check the chain. Read the steps as a story. Does each step have what it needs from the earlier ones? Does the last step produce the required output?
  4. Split steps that are still big. A step that you cannot imagine writing in a few lines needs to be split again.
  5. Solve each step separately, then join them.

Here is a useful test for any step: can I describe it in one sentence without using the word "and"? "Add up the valid grades" passes. "Read the grades and check them and add them up" does not, so it is really three steps.

How small should steps be?

A step is small enough when you know how to do it, or could write it in a few lines of Java using what you have learned. It is too big when you find yourself saying "somehow". It is too small when it merely repeats the syntax, such as "write an opening brace". Aim for steps at the level of a sentence you would say to a friend.

Steps become methods

You learned that a method has a name, parameters, and a return value. A well-chosen step is often exactly that: its inputs are the parameters, its result is the return value, and its one-sentence description is the name.

StepA possible method
Check whether a grade is validboolean isValid(int grade)
Add up the valid gradesint sumValid(int[] grades)
Count the valid gradesint countValid(int[] grades)
Work out the averagedouble average(int[] grades)

Methods built from steps are easier to test, because you can run one step alone and see whether it behaves, and easier to read, because average calls sumValid and countValid, and reads like the plan.

Not every step needs its own method. A one-line step that is used only once can stay inside another method. Use the plan to decide: if a step has a clear name, a clear input, and a clear result, a method is a good fit.

The decomposition can be shown as a tree

Sometimes steps contain sub-steps. A short outline works well:

text
Report the average of valid grades
  1. Read the grades
  2. Compute the total of valid grades
       2a. For each grade, check if it is valid
       2b. Add it to the total if it is
  3. Count the valid grades
  4. Divide the total by the count
  5. Print the result

Steps 2a and 2b came from splitting step 2 when it was still big. The outline is a map you can follow, and it is also a checklist: when every line works, the problem is solved.

Examples

Example 1: a receipt total (simple)

Problem: "Given the prices of the items in a basket and a tax percentage, print the total to pay."

Restated: input is a list of prices and a tax percent; output is one number.

text
1. Add up the prices (the subtotal).
2. Work out the tax: subtotal times the percentage, divided by 100.
3. Add the tax to the subtotal.
4. Print the result.

Check the chain: step 2 needs the subtotal from step 1, step 3 needs both earlier results, and step 4 prints the final value. Every step passes the "one sentence, no and" test. This problem needs no further splitting, and a nice sign of that is that you can already imagine the code for each line.

Example 2: average of valid grades (practical)

Problem: "Given an array of grades, report the average of the valid ones." Valid means from 0 to 100 inclusive. Decision recorded from the previous lesson's habit: if there are no valid grades, the average is 0.

The steps from the outline above become four small methods and one calling method:

java
public class GradeReport {
    static boolean isValid(int grade) {
        return grade >= 0 && grade <= 100;
    }

    static int sumValid(int[] grades) {
        int sum = 0;
        for (int g : grades) {
            if (isValid(g)) sum += g;
        }
        return sum;
    }

    static int countValid(int[] grades) {
        int count = 0;
        for (int g : grades) {
            if (isValid(g)) count++;
        }
        return count;
    }

    static double average(int[] grades) {
        int count = countValid(grades);
        if (count == 0) return 0;
        return (double) sumValid(grades) / count;
    }

    public static void main(String[] args) {
        int[] grades = {80, 105, 70, -5, 90};
        System.out.println(sumValid(grades));
        System.out.println(countValid(grades));
        System.out.println(average(grades));
    }
}

Real output:

text
240
3
80.0

Check it by hand: the valid grades are 80, 70, and 90, whose sum is 240; there are three of them; 240 divided by 3 is 80. The grades 105 and -5 were skipped. Because the work was split, you could test isValid(100) and isValid(101) alone before running the whole program. Note the cast (double): without it, the division would be integer division and drop the decimals, as you learned in the operators module.

Example 3: the longest word in a sentence (realistic)

Problem: "Given a sentence, print its longest word and its length." Assumptions: words are separated by single spaces, and if two words tie, the first one wins.

First pass of the decomposition:

text
1. Cut the sentence into words.
2. Pick the longest word.
3. Print the word and its length.

Step 2 is still a bit big, because "pick the longest" hides a loop and a comparison. Split it:

text
2a. Start by assuming the first word is the best so far.
2b. For each word, if it is longer than the best so far, it becomes the best.

Now every step maps to code you already know: split, a loop, length(), and an if.

java
public class LongestWord {
    static String longestWord(String sentence) {
        String[] words = sentence.trim().split(" ");
        String best = words[0];
        for (String w : words) {
            if (w.length() > best.length()) best = w;
        }
        return best;
    }

    public static void main(String[] args) {
        String w = longestWord("plan before you code");
        System.out.println(w + " " + w.length());
    }
}

Real output:

text
before 6

The code mirrors the steps: the first line of the method is step 1, the loop is 2a and 2b, and main is step 3. Because the comparison uses > and not >=, a tie keeps the earlier word, which is the decision we recorded. This version still has gaps (an empty sentence, extra spaces between words), and the lesson on edge cases will return to them.

Backend Insight

Backend code is decomposed constantly. A request to place an order is never one block of code. It becomes a chain: read the request, check that it is valid, check stock, calculate the price, save the order, build the response. Each link becomes its own method, and in later modules these methods are grouped into classes and layers.

The benefits are practical. A team can work on different steps at the same time. A failing step can be found by asking "which step produced the wrong result?" instead of searching all the code. And one step can change (a new tax rule, say) without rewriting the others.

Common Mistakes

Mistake 1: Steps that are too big

  • Incorrect: "1. Calculate the answer. 2. Print it."
  • Correct: Steps that each tell you what to do, such as "add up the valid grades" and "divide by the count".

Why it happens: the first idea feels like a plan. How to avoid it: if you cannot picture the code for a step, split it.

Mistake 2: Hidden "and"

A step like "read the grades and ignore bad ones" is two steps stuck together, and the second one is often forgotten in the code. Give each action its own line.

Mistake 3: Steps in the wrong order

  • Incorrect: "Divide the total by the count" before the count exists.
  • Correct: Read the steps as a story and check that each step has what it needs from the lines above.

Mistake 4: Splitting too much

Writing a separate method for every single line makes the program harder to read, not easier. Split until each step is clear, then stop.

Mistake 5: Joining without testing the parts

Writing all the steps and running them only together means a wrong answer could come from any of them. Check each step on a tiny example, then join them.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. What is decomposition, in one sentence?

Show solution A1

A1. Breaking a big problem into small steps that can each be solved on their own, then joining them.

A2. Is "read the number and check it and print it" a good single step? Explain, and rewrite it.

Show solution A2

A2. No. It contains the word "and" twice, so it is three steps: read the number, check the number, print the number.

A3. Put these steps of "print the sum of the even numbers in an array" into a correct order: (a) print the sum, (b) set the sum to 0, (c) for each number, if it is even then add it to the sum.

Show solution A3

A3. (b), (c), (a).

Level B: Application

B1. Decompose "count how many numbers in an array are negative" into at most five steps.

Show solution B1

B1. 1. Start a counter at 0. 2. Look at each number. 3. If it is below 0, increase the counter. 4. After all numbers, print the counter. (Four steps; the loop is steps 2 and 3.)

B2. Decompose "print the initials of a full name such as Ada King Lovelace" into steps. State your assumption about how the words are separated. Then name two methods the steps could become, with their signatures.

Show solution B2

B2. Assumption: words are separated by one space and there are no extra spaces. Steps: 1. Split the name into words. 2. For each word, take its first character. 3. Join the characters. 4. Print the result. Possible methods: static String[] splitName(String name) (or just use split directly) and static String initials(String fullName).

B3. Here is a decomposition of "print the largest score and who got it": (1) read names and scores, (2) find the largest score, (3) print it. What step is missing, and what does the missing step need?

Show solution B3

B3. The step that keeps who got the largest score is missing. Finding the largest score alone loses the name, so the step must remember the position (index) of the largest score, or the name beside it, while looping. Printing needs both the name and the score.

Level C: Challenge

C1. Decompose "check whether an array of ints is sorted in ascending order" into steps, including a sub-split of the step that is still big. Then write the method static boolean isSortedAscending(int[] a) and run it on {1, 2, 2, 5} and {1, 3, 2}.

Show solution C1

C1. Steps: 1. For each neighbouring pair (a[i], a[i+1]), check the order. 2. If any pair is out of order, the answer is false. 3. If all pairs pass, the answer is true. Step 1 splits into: 1a. visit each position from 0 up to the second-to-last, 1b. compare it with the next. Code:

java
static boolean isSortedAscending(int[] a) {
    for (int i = 0; i < a.length - 1; i++) {
        if (a[i] > a[i + 1]) return false;
    }
    return true;
}

Running it prints true for {1, 2, 2, 5} (equal neighbours are allowed here, a decision we made) and false for {1, 3, 2}.

C2. Decompose "give a shopping basket a delivery fee: free above 50, otherwise 5, plus 2 for every kilogram over 10". List the inputs, then the steps, then which steps would be methods.

Show solution C2

C2. Inputs: the basket total (double) and the weight in kilograms (double). Steps: 1. Start the fee at 0. 2. If the total is not above 50, add 5. 3. If the weight is above 10, add 2 for every kilogram over 10. 4. Return the fee. Candidate methods: baseFee(total) for step 2, weightFee(weight) for step 3, and deliveryFee(total, weight) joining them. (Whether "every kilogram" means whole kilograms is a question to ask.)

Mini Challenge

Choose this problem: "Given the scores of a game as an array, print how many scores are above the average."

  1. Restate it with input, output, and constraints.
  2. Decompose it into numbered steps, splitting any step that is still big.
  3. Mark which steps you would turn into methods and write their signatures.
  4. Solve a small example by hand using your steps.

Check your work: every step passes the "one sentence, no and" test, the order is valid, and you can point to where the average is used.

Summary & Next Lesson

Key takeaways

  • Decomposition turns a big problem into a chain of small steps, like the legs of a trip.
  • Write each step as one plain sentence. If it needs "and", split it.
  • Check that the steps in order turn the inputs into the outputs.
  • Split a step again if you cannot picture its code.
  • A well-chosen step often becomes a method. Test the parts, then join them.

Next lesson

You now have a list of steps in plain sentences. The next lesson, "Pseudocode and flowcharts", shows two ways to write the steps with more structure (loops, decisions) before writing Java.

Self-check

  1. What do you start from when you decompose a problem?
  2. What test tells you a step is really two steps?
  3. What does a step often become in code?
  4. A step says "somehow find the answer". What should you do?
  5. Why test each small step before joining them?
Show answers
  1. The restatement: the inputs, the outputs, and the constraints.
  2. If describing it needs the word "and", it contains more than one action.
  3. A method, with the step's inputs as parameters and its result as the return value.
  4. Split it into smaller steps until you can picture the code for each.
  5. If you test only the whole, a wrong answer could come from any step. Testing each step finds the cause quickly.

References

  • Oracle, The Java Tutorials: Defining Methods. Supports the idea that a method has a name, parameters, and a return value, which is how steps map to methods in this lesson.
  • Oracle, Java SE 21 API: String. Supports split, trim, and length used in the longest-word example.
  • The Java examples were compiled and run with JDK 21 and the output shown is real. The decomposition routine is a general teaching practice; no single source is cited for it.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.