Writing Readable Methods and Classes
A good recipe book doesn't print one giant paragraph that says "make biryani". It splits the work into steps: soak the rice, marinate the chicken, fry the onions, layer and cook. Each step has a clear name and you can follow it without reading the whole book. Readable Java code works the same way: small methods with clear names, grouped into classes that each have one job. In this lesson, you will learn the rules that make methods and classes easy to read, and see a long method turned into a set of small ones without changing what the program prints.
What Makes a Method Readable?
A readable method in Java is a method that a teammate can understand from its name and a quick look at its body, without scrolling or guessing. The main rules are:
- Does one thing. If you need "and" to describe it (
validateAndSaveAndEmail), it does too much. - Has a name that says what it does.
calculateGrade(marks)beatsprocess(m). - Is short. Most methods fit on one screen; many are 5 to 15 lines. Length isn't the goal, though; one clear job is.
- Takes few parameters. Zero to three is easy to read. Four or more usually means some of them belong together in an object.
- Stays at one level of detail. A method that reads like a summary ("read marks, work out the grade, print the card") shouldn't also contain low-level loops and string formatting.
A Long Method
Let's see a method that works but does everything in one place.
public class ReportCard {
public static void main(String[] args) {
print("Navika Anamaneni", new int[] {91, 84, 77, 95, 88});
}
static void print(String name, int[] m) {
int t = 0;
for (int x : m) {
t += x;
}
double a = (double) t / m.length;
String g;
if (a >= 90) {
g = "A+";
} else if (a >= 80) {
g = "A";
} else if (a >= 70) {
g = "B";
} else {
g = "C";
}
int best = m[0];
for (int x : m) {
if (x > best) {
best = x;
}
}
System.out.println("Student : " + name);
System.out.println("Total : " + t + " / " + m.length * 100);
System.out.printf("Average : %.1f%n", a);
System.out.println("Grade : " + g);
System.out.println("Best : " + best);
}
}Student : Navika Anamaneni
Total : 435 / 500
Average : 87.0
Grade : A
Best : 95It is only 30 lines, yet to understand it you must read every line: the names t, a, g and m don't say anything, and adding, averaging, grading, finding the best mark and printing are all mixed together.
The Same Program, Split into Small Methods
public class ReportCard {
private static final int MAX_MARKS_PER_SUBJECT = 100;
public static void main(String[] args) {
print("Navika Anamaneni", new int[] {91, 84, 77, 95, 88});
}
static void print(String name, int[] marks) {
int total = total(marks);
double average = (double) total / marks.length;
System.out.println("Student : " + name);
System.out.println("Total : " + total + " / "
+ marks.length * MAX_MARKS_PER_SUBJECT);
System.out.printf("Average : %.1f%n", average);
System.out.println("Grade : " + gradeFor(average));
System.out.println("Best : " + best(marks));
}
static int total(int[] marks) {
int sum = 0;
for (int mark : marks) {
sum += mark;
}
return sum;
}
static String gradeFor(double average) {
if (average >= 90) return "A+";
if (average >= 80) return "A";
if (average >= 70) return "B";
return "C";
}
static int best(int[] marks) {
int best = marks[0];
for (int mark : marks) {
best = Math.max(best, mark);
}
return best;
}
}Student : Navika Anamaneni
Total : 435 / 500
Average : 87.0
Grade : A
Best : 95Explanation:
printnow reads like a summary: work out the total and average, then print each line. The details live in methods whose names tell you what they return.total,gradeForandbesteach do one thing, so each is easy to test on its own. You can checkgradeFor(89.9)without building a report card.gradeForuses early returns instead of anif-elsechain that assigns a variable. Each line reads as a rule.- The number
100became the constantMAX_MARKS_PER_SUBJECT. - The output is identical. Changing structure without changing behavior is called refactoring.
Rules for Parameters
| Problem | Better |
|---|---|
Too many parameters: register(name, age, city, phone, email, course) | Group them in a record: register(StudentDetails details) |
Boolean flags: print(card, true), where nobody knows what true means | Two methods: printSummary(card) and printFull(card) |
| Output parameters: a method that changes the list you pass in | Return a new value instead |
null as "no value": find(name, null) | An overloaded method without that parameter, or Optional for return values |
Guard Clauses Instead of Deep Nesting
A guard clause checks for a special case at the start and returns early, so the main logic isn't buried inside several if levels.
// Nested: the real work is three levels deep
String admit(Student s) {
if (s != null) {
if (s.age() >= 5) {
if (s.feePaid()) {
return "Admitted";
} else {
return "Fee pending";
}
} else {
return "Too young";
}
} else {
return "No student";
}
}
// Guard clauses: special cases first, main path last
String admit(Student s) {
if (s == null) return "No student";
if (s.age() < 5) return "Too young";
if (!s.feePaid()) return "Fee pending";
return "Admitted";
}What Makes a Class Readable?
A readable class has one responsibility: one reason to change. This is the Single Responsibility Principle from the SOLID lesson.
| Sign of trouble | What to do |
|---|---|
The class name is vague: Manager, Helper, Utils, Processor | Name it after its one job: FeeCalculator, MarksheetPrinter |
| It has hundreds of lines and dozens of fields | Split it by responsibility |
| Some methods use only some fields, others use only other fields | Those groups are probably two classes |
| Changing the report layout and changing the fee rules both edit the same class | Put layout and rules in separate classes |
A class whose methods all work with the same fields is called cohesive. Cohesive classes are easier to name, test and reuse.
In Practice
- Name first. If you can't find a short, honest name for a method or class, it probably does more than one thing.
- Read it aloud.
if (student.hasPaidFees())reads like English;if (s.f == 1)doesn't. - Let the IDE help. Extract Method (Ctrl+Alt+M in IntelliJ IDEA) and Rename (Shift+F6) make these changes safely in seconds.
- Keep tests running. Small methods are easy to test, and tests make splitting safe.
Common Mistakes
Splitting too far. Twenty one-line methods that only call each other are as hard to follow as one long method. Split where a step has a clear name.
Names that lie. A method called getTotal that also saves to the database surprises everyone. Names must cover everything the method does.
Comment headers instead of methods. // --- calculate grade --- above a block is a sign that the block wants to be a method called calculateGrade.
God classes. One SchoolManager class that handles students, fees, timetables and reports becomes impossible to change safely.
Practice
- Easy: Add a method
worst(int[] marks)to the second program and print it as a new line "Lowest :". - Harder: The
printmethod still mixes calculation and printing. Create a recordResult(String name, int total, double average, String grade, int best), a methodResult evaluate(String name, int[] marks), and a methodprint(Result result). The output must stay identical.
Key Takeaways
Was this tutorial helpful?