Menu

Abstraction and Decomposition

Contents

  1. Computational Thinking
  2. Decomposition
  3. Benefits of Decomposition
  4. Abstraction
  5. Subprograms

1. Computational Thinking

Computational thinking is the collection of techniques you use to analyse a problem, understand it and work out how it could be solved by a computer. It often happens before any code is written, because you need to know what the computer is being asked to do before you decide how to program it.

The techniques it covers

TechniqueWhat it does
DecompositionBreaks something down into smaller parts.
AbstractionRemoves or hides the detail that is not needed.

A large, messy problem cannot be tackled in one go. Decomposition gives you pieces small enough to hold in your head, and abstraction strips each piece back to what actually matters for the solution. Between them, a vague idea becomes something you can plan, design and build.

For example, suppose your year group is selling tickets for a film night and you are writing the program that handles it. Decomposition splits the job into separate parts, such as working out the ticket price, recording a booking and counting the seats still free. Abstraction then lets each part ignore what it does not need: working out the price needs the buyer's age and whether they are a student, but not their name or which seat they chose.

Because they help you analyse, understand and solve problems, decomposition and abstraction turn up again and again in Computer Science, and most solutions you build will lean on both.

2. Decomposition

Decomposition means breaking a problem, a solution or an algorithm down into smaller parts. Instead of facing one huge task, you work on several small ones that you can get your head around.

Each smaller part is called a sub-problem, also known as a subtask. It is still a job in its own right, just small enough to think about separately from the rest.

Breaking a problem down

Imagine you must write a program for a school book club that shows which member has read the most books. As one single task that is daunting, but decomposed it becomes:

  • store each member's name and how many books they have read
  • find the highest number of books read
  • print that number with the member's name beside it

Each of those is a sub-problem, and any of them can be broken down again. "Find the highest number" becomes: take the first count as the best so far, compare each remaining count in turn, and replace the best whenever you meet a bigger one. That step is now small enough to turn into code straight away.

When to stop

Decomposition does not have to carry on until every task is a single line of code. You stop when each remaining task is manageable — easy to work with and clearly understood. A block of twenty lines that does one clear job is perfectly manageable, so there is no need to split it further.

Decomposition is not only for problems, either. A finished solution and the algorithm behind it can be split up in the same way: a program that controls a school greenhouse's watering divides into reading the soil moisture, deciding whether watering is needed, and opening the valve for the right length of time, and each part can be written, tested and improved on its own.

The program below is a solution broken into small blocks: one works out the best count and another uses it to report the champion.

def findBest(counts):
    best = counts[0]
    for number in counts:
        if number > best:
            best = number
    return best

def showChampion(names, counts):
    best = findBest(counts)
    for i in range(len(counts)):
        if counts[i] == best:
            print(names[i] + " read " + str(best) + " books")

names = ["Aisha", "Ben", "Chi", "Dara", "Eve"]
counts = [4, 9, 6, 7, 2]
showChampion(names, counts)

Output:

Ben read 9 books

3. Benefits of Decomposition

Decomposition is breaking a large problem down into smaller tasks. Splitting a problem up is worth the effort because the smaller tasks are far easier to solve than tackling the whole thing in one go.

Small tasks are easier to solve

When you work on a small task, you can hold everything you need to know in your head at once. Imagine you are writing a program for a school sports day. It has to store each runner's time, work out the fastest runner and print the winners. As one job that is a lot to think about. Broken down, the tasks are just "collect the times", "find the fastest time" and "print the results", and each one on its own is straightforward.

Work can be shared out

Because the tasks are separate, they can be handed to different people. One person writes the part that collects the times while another writes the part that finds the fastest time, so several people work at the same time and the solution is finished sooner.

Less wasted analysis, fewer errors

Focusing on one small part also means you do not spend time analysing detail that is not relevant to it. The person finding the fastest time does not need to worry about how the times were typed in. Each small part can be tested on its own, and when the tested parts are combined into the single larger solution it contains fewer errors than if it had been written in one go.

4. Abstraction

Abstraction is removing or hiding unnecessary detail from a problem so that attention can be focused on the essential components. When you abstract, you decide what really matters for the problem you are solving and leave everything else out.

Keeping only what matters

Abstraction is not about losing information by accident. It is a deliberate choice to keep the detail the problem needs and ignore the rest. Imagine you are writing a program for a school library that works out when a book is overdue. The date the book was borrowed and the number of days it can be kept are essential. The colour of the book's cover, who printed it and how heavy it is are not needed, so they are abstracted away.

Simplified models

Abstraction is how you build simplified models of real-world objects and systems that a program can work with. A model is a version of something real that keeps only the parts the program needs.

For example, a program that runs a school vending machine might model what the machine holds. The model stores the number of each slot, the snack in that slot, its price and how many are left. It ignores the colour of the machine's casing, who made it and what the buttons feel like, because none of that helps work out what should happen when a pupil types B4 and pays.

A model like this is much smaller and simpler than the real vending machine, which makes it quicker to work with and easier to program. The program can then answer the real question without being buried in details that make no difference to the answer.

5. Subprograms

A subprogram is a separate block of code that performs one identifiable task. It has a name, and when the program needs that task doing, it calls the subprogram by name.

When you decompose a problem, you end up with a list of tasks. Each manageable task can be turned into a subprogram, so the code for one job sits together in one place instead of being scattered through the main program. Splitting a program into a main program plus separate blocks of logic is itself decomposition at the code level.

Built-in, library or your own

Subprograms can come from three places.

  • Built into the language, such as len(), which gives the number of items in a list. It is ready to use and you did not write it.
  • Supplied by a library, such as math.sqrt(), which you can call once you have written import math. Someone else wrote it.
  • Written by the programmer in the same program file, using def. This is the block you create for a task your decomposition found.
def isMultipleOf(number, factor):
    # one task: is number a multiple of factor?
    if number % factor == 0:
        return True
    else:
        return False

print(isMultipleOf(21, 7))
print(isMultipleOf(20, 7))

Output:

True
False

Here isMultipleOf does one identifiable job: it works out whether one number is a multiple of another and returns a True or False answer. The main program calls it twice, passing different arguments each time.

The list of tasks from your decomposition has not changed, but the code now shows clearly which block does which job.