What do coupling and cohesion mean in software design, and what do good values look like in Java?
Coupling measures how much one class depends on another. Cohesion measures how closely the responsibilities inside one class belong together. Good design aims for low coupling and high cohesion: classes depend on each other through narrow contracts, and each class does one job well.
public class Main {
public static void main(String[] args) {
Report r = new Report(new PlainFormatter());
r.addLine("alpha");
System.out.println(r.render());
}
}
interface Formatter {
String format(String line);
}
class PlainFormatter implements Formatter {
public String format(String line) { return line; }
}
class Report {
private final Formatter formatter;
private final java.util.List<String> lines = new java.util.ArrayList<>();
Report(Formatter formatter) { this.formatter = formatter; }
void addLine(String line) { lines.add(line); }
String render() {
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(formatter.format(line)).append("\n");
}
return sb.toString();
}
}The report class shows both. It is cohesive, because everything it does is about producing a rendered report. It is loosely coupled, because it depends on the formatter interface rather than on a concrete formatting class. The plain formatter can be replaced with one that upper-cases, prefixes or emits markup, and the report class does not change.
The alternative to look at is the same class hard-coding a formatter. Then the report knows about the concrete class, cannot be tested with a fake, and has to be edited when the output format changes. That is higher coupling, and the change ripples outwards.
These two goals pull against each other in one place. Splitting a class to reduce coupling can create classes that each do too little, and cohesion suffers. The way to resolve it is to split along responsibilities rather than along arbitrary lines, so each new class has a reason to exist and a single thing it is about.