Decomposing a problem into modules and classes
A module is a separate part of a program with one job. To decompose a problem:
- Write what the whole program must do.
- List the main things (nouns) in the problem: members, books, loans. Each can become a class that holds its data and its methods.
- List the main jobs (verbs): add, search, issue, return. Each becomes a method or function.
- Draw a structure chart (a tree): the program at the top, modules below, subprograms below them.
Two quality checks: high cohesion (everything in a module belongs to one job) and low coupling (modules depend on each other as little as possible).
Functional decomposition in subprogram design
Functional decomposition is top-down design: take a task and break it into smaller sub-tasks, then break those again, until each piece is small enough to write as one short subprogram (function, procedure or method).
issueBook(memberId, bookId) โโ checkMember(memberId) โโ checkAvailable(bookId) โโ recordLoan(memberId, bookId, dueDate) โโ printSlip()
Each subprogram has clear parameters (inputs) and a return value (output). Good rules: one task per function, a name that is a verb, usually less than a screen long, and no hidden use of global variables.
Programmers also use stubs (empty placeholder functions) to test the top level before the lower parts are written.
Data encapsulation in program design
Encapsulation means putting data and the code that uses it together, and hiding the data from the outside. In most languages you mark fields private and give public methods (the interface).
class Account {
private double balance;
public void deposit(double x) { if (x > 0) balance += x; }
public double getBalance() { return balance; }
}Nobody outside can set the balance to โ500, because the only way in is deposit(), which checks the value. Benefits: data stays valid, the inside can be changed later without breaking other code, and each module is easier to understand.
Reusability in program design
Reusability means writing a module once and using it in many places or programs. Ways to make code reusable:
- Use parameters instead of fixed values (a
sort(list)that works for any list). - Keep modules general and independent (low coupling).
- Put shared code in a library or package; use inheritance or composition for classes.
- Document the interface: what goes in, what comes out.
Reuse saves time, cuts bugs (tested code is used again), and keeps programs consistent. Almost every app uses library modules for things like dates, maths and networking.
Try it
Unplugged: write "Make breakfast" at the top of a page. Break it into 3 modules, then each into 2โ3 steps. Circle any step you could reuse for "Make lunch".
In the 3D: press Split twice and count the pieces (1 โ 3 โ 6). Then press Encapsulate and Reuse.
Key formulas and definitions
- Module: a separate part with one job
- Functional decomposition: task โ sub-tasks โ subprograms (top-down)
- Encapsulation: private data + public methods (interface)
- Good design: high cohesion, low coupling
- Reusability: write once, use many times (libraries, parameters)
- Structure chart: a tree of modules and subprograms
Worked examples
1. Decompose a school report-card program into modules.
Modules: Students (store names and IDs), Marks (enter and check marks), Calculations (total, average, grade), Report (format and print). Each has one job, so each can be tested alone.
2. Break the task 'calculate a student's grade' into subprograms.
readMarks(id) โ total(marks) โ average(total, count) โ gradeFor(average) โ return grade. Each function takes inputs and returns one result.
3. Why should 'balance' in a BankAccount class be private?
So no other code can set it to a wrong value. All changes go through deposit() and withdraw(), which check the amount. The data stays valid.
4. A function computes area = 3.14 ร 5 ร 5. How can you make it reusable?
Add a parameter: area(r) returns 3.14159 ร r ร r (or uses the language's pi). Now any radius works and the function can be put in a maths module.
5. Module A reads module B's variables directly, and B reads A's. What is wrong and how do you fix it?
Coupling is high: changing one breaks the other. Fix: make the variables private and pass data through method calls with parameters and return values.
Common mistakes
- Making one huge function that does everything. Split it so each function has one task.
- Using global variables to share data between modules. Pass parameters and return values instead.
- Making all fields public. That breaks encapsulation; keep data private and give methods.
- Splitting too far, into dozens of one-line functions with unclear names. Stop when each piece is clear and testable.