6.1.1 Decomposition and abstraction in code
From Problem to Code
Decomposition means breaking a complex problem into smaller, manageable sub-problems. Abstraction means focusing on the essential features of each sub-problem while hiding unnecessary detail. Together, they allow a programmer to tackle large problems by writing focused, reusable subprograms — rather than one unmanageable block of code.
In code, decomposition is implemented through functions and procedures: each sub-problem becomes its own subprogram with a clear purpose, defined inputs and (for functions) a returned result.
Worked Example: Calculating a Bill
Problem: a café system needs to calculate a customer's total bill, apply a discount if they are a member, and print a receipt. Decomposed into three sub-problems: calculate subtotal, apply discount, print receipt.
def calculate_subtotal(items):
"""Returns the sum of a list of item prices."""
total = 0
for price in items:
total = total + price
return total
def apply_discount(subtotal, is_member):
"""Returns the discounted total if the customer is a member."""
DISCOUNT_RATE = 0.10 # 10% member discount
if is_member:
return subtotal * (1 - DISCOUNT_RATE)
else:
return subtotal
def print_receipt(subtotal, final_total, is_member):
"""Prints a formatted receipt."""
print("--- Receipt ---")
print("Subtotal: £" + str(round(subtotal, 2)))
if is_member:
print("Member discount applied (10%)")
print("Total: £" + str(round(final_total, 2)))
# Main program
items = [3.50, 1.20, 4.75]
subtotal = calculate_subtotal(items)
total = apply_discount(subtotal, True)
print_receipt(subtotal, total, True)
Each function does one job. apply_discount() does not need to know how the subtotal was calculated — it just uses the value passed to it. This is abstraction in action.
using System;
using System.Collections.Generic;
class CafeSystem {
static double CalculateSubtotal(List<double> items) {
double total = 0;
foreach (double price in items) {
total += price;
}
return total;
}
static double ApplyDiscount(double subtotal, bool isMember) {
const double DISCOUNT_RATE = 0.10;
if (isMember)
return subtotal * (1 - DISCOUNT_RATE);
else
return subtotal;
}
static void PrintReceipt(double subtotal, double finalTotal, bool isMember) {
Console.WriteLine("--- Receipt ---");
Console.WriteLine("Subtotal: £" + Math.Round(subtotal, 2));
if (isMember)
Console.WriteLine("Member discount applied (10%)");
Console.WriteLine("Total: £" + Math.Round(finalTotal, 2));
}
static void Main() {
var items = new List<double> { 3.50, 1.20, 4.75 };
double subtotal = CalculateSubtotal(items);
double total = ApplyDiscount(subtotal, true);
PrintReceipt(subtotal, total, true);
}
}
Module CafeSystem
Function CalculateSubtotal(items As List(Of Double)) As Double
Dim total As Double = 0
For Each price As Double In items
total = total + price
Next
Return total
End Function
Function ApplyDiscount(subtotal As Double, isMember As Boolean) As Double
Const DISCOUNT_RATE As Double = 0.10
If isMember Then
Return subtotal * (1 - DISCOUNT_RATE)
Else
Return subtotal
End If
End Function
Sub PrintReceipt(subtotal As Double, finalTotal As Double, isMember As Boolean)
Console.WriteLine("--- Receipt ---")
Console.WriteLine("Subtotal: £" & Math.Round(subtotal, 2))
If isMember Then
Console.WriteLine("Member discount applied (10%)")
End If
Console.WriteLine("Total: £" & Math.Round(finalTotal, 2))
End Sub
Sub Main()
Dim items As New List(Of Double) From {3.50, 1.20, 4.75}
Dim subtotal As Double = CalculateSubtotal(items)
Dim total As Double = ApplyDiscount(subtotal, True)
PrintReceipt(subtotal, total, True)
End Sub
End Module
Benefits of Decomposition in Code
- Easier to write: each function is a small, focused problem — simpler to reason about than one large block
- Easier to test: each function can be tested independently with known inputs and expected outputs
- Reusable:
apply_discount()could be used in other parts of the program (e.g. a takeaway system) without rewriting the logic - Easier to maintain: if the discount rate changes, only one function needs editing — not every place discount logic appears
- Readable: the main program reads like a description of the task: calculate, apply discount, print
Key Takeaways
- Decomposition breaks a problem into sub-problems; each becomes a function or procedure in code.
- Abstraction hides detail — a function's caller only needs to know what it does, not how.
- Functions should do one job and have a clear, descriptive name.
- Decomposed code is easier to write, test, reuse and maintain than monolithic code.