Modular design is the practice of decomposing a complex algorithm or system into smaller, self-contained units called modules (or functions/subroutines). Each module:
- Has a clear, single responsibility
- Communicates with other modules only through a defined interface
- Can be developed, tested, and replaced independently
KEY TAKEAWAY: Modular design reduces complexity by breaking large problems into manageable pieces. It is the algorithmic equivalent of “divide and conquer” at the design level.
| Benefit | Explanation |
|---|---|
| Readability | Shorter, focused functions are easier to understand |
| Reusability | A function defined once can be called many times |
| Testability | Each module can be tested independently |
| Maintainability | Bug fixes are localised — changing one module does not break others |
| Collaboration | Multiple developers can work on different modules simultaneously |
Functions (subroutines) are the primary mechanism for modularisation:
FUNCTION findMin(A)
min ← A[0]
for i ← 1 to length(A) - 1 do
if A[i] < min then
min ← A[i]
end if
end for
return min
FUNCTION findMax(A)
max ← A[0]
for i ← 1 to length(A) - 1 do
if A[i] > max then
max ← A[i]
end if
end for
return max
ALGORITHM Range(A)
return findMax(A) - findMin(A)
The Range algorithm delegates to findMin and findMax without duplicating logic.
ADTs enable modular design at the data level:
ALGORITHM ShortestPath(graph, source)
// Uses: Graph ADT, Priority Queue ADT, Dictionary ADT
dist ← Dictionary.empty()
pq ← PriorityQueue.empty()
PriorityQueue.insert(pq, source, 0)
Dictionary.insert(dist, source, 0)
while NOT PriorityQueue.isEmpty(pq) do
v ← PriorityQueue.removeMin(pq)
for each (w, weight) in Graph.neighbours(graph, v) do
...
end for
end while
return dist
Each ADT (Graph, Priority Queue, Dictionary) is a module. The algorithm works with any correct implementation of each ADT.
Problem: Plan the shortest delivery route
├── Graph ADT module (represent city road network)
├── Priority Queue ADT module (support greedy selection)
├── Dijkstra's module (uses Graph + Priority Queue)
└── Main module (reads input, calls Dijkstra's, outputs route)
Functional abstraction means replacing a specific sequence of operations with a named function. Instead of:
// Repeated 5 times in the algorithm:
sum ← 0
for i ← 0 to length(A)-1 do
sum ← sum + A[i]
end for
Define once:
FUNCTION sum(A)
total ← 0
for each x in A do
total ← total + x
end for
return total
And call it as needed: totalCost ← sum(costs)
EXAM TIP: VCAA exams may ask you to write a modular algorithm using ADTs and functions. Show explicit function definitions and calls — do not inline everything into one monolithic block.
VCAA FOCUS: Be able to justify modular design choices: why did you choose this function interface? What does it abstract away? How does it enable reuse?