Unit 06 · lesson
A Method Is a Contract, Not a Code Drawer
It is easy to describe methods as a way to "avoid repeating code." Reuse matters, but it is not the most important idea.
A method creates a named boundary around a responsibility.
A method creates a responsibility boundary
Parameters define incoming information while return values or effects define the method promise.
- CALLERneeds one focused operationpass
- PARAMETERSdeclare incoming values and typesenter
- METHOD BODYperform the bounded responsibilityproduce
- RETURN / EFFECTsend back a value or create an explicit effectverify
- CONTRACT EVIDENCEtest normal, boundary, and invalid cases
int clampScore(int score) {
if (score < 0) return 0;
if (score > 100) return 100;
return score;
}
You can describe its contract:
input: an int score
output: an int in the inclusive range 0..100
rule: values below 0 become 0; values above 100 become 100
That description gives you test cases before you inspect the implementation.
Return type is part of the promise
double average(int total, int count) {
return (double) total / count;
}
The method promises a double result. But what happens when count == 0?
The signature alone cannot express every domain rule. The method contract must include preconditions or explicit failure behavior.
A better first design might be:
double average(int total, int count) {
if (count <= 0) {
throw new IllegalArgumentException("count must be positive");
}
return (double) total / count;
}
You have not formally studied exceptions yet. For now, notice the design improvement: invalid use is visible rather than silently producing misleading data.
void methods have effects instead of returned values
void printBanner(String name) {
IO.println("=== " + name + " ===");
}
This method returns no value. Its observable effect is output.
Methods that calculate and return data are often easier to test than methods that mix calculation, input, output, and mutation.
Compare:
double percent(int passed, int total) {
return (double) passed / total * 100;
}
with a giant method that reads input, calculates, formats, and prints everything. Separating calculation gives the core rule a clean test boundary.
Name the responsibility
Weak names:
doStuff
process
helper
thing
Stronger names tell you what the contract is about:
calculatePassRate
isValidScore
normalizeCommand
formatReportLine
A good name cannot rescue a method that performs five unrelated jobs. If the sentence describing the responsibility needs multiple "and then" clauses, the method may be doing too much.
Extract a method deliberately
Start with:
void main() {
int passed = 18;
int total = 24;
if (total <= 0) {
IO.println("NO DATA");
} else {
double percent = (double) passed / total * 100;
IO.println(percent);
}
}
Decide what can become a calculation method and what remains orchestration in main.
Do not blindly extract every line. Preserve meaningful responsibility boundaries.
Evidence
Create one method and document its contract with:
- purpose;
- parameter meaning;
- return value or effect;
- valid input range;
- one boundary case;
- one invalid case or precondition.
Write expected results for the cases before running them. Then execute and compare.