17 · 32 · 6 min
Single responsibility, open closed, Liskov
SOLID names five ways a design goes wrong. The first three.
S: single responsibility
A class should have one reason to change.
class Report {
String generate() { } // changes when the report changes
void saveToPdf() { } // changes when PDF handling changes
void email() { } // changes when the mail server changes
}Three reasons to change, so three classes. The test is not "does it do one thing", which is impossible to judge, but who asks for changes to it. If accounting and IT both file bugs against one class, it has two responsibilities.
O: open closed
Open for extension, closed for modification. The page-one chain broke this: a new channel meant editing a working file. The interface fixed it.
The symptom is always the same complaint: *"we keep breaking X while adding Y."* That only happens when new work forces you to touch old code.
L: Liskov substitution
A subclass must be usable anywhere its parent is, without surprising anyone.
class Rectangle {
void setWidth(int w) { this.w = w; }
void setHeight(int h) { this.h = h; }
int area() { return w * h; }
}
class Square extends Rectangle {
void setWidth(int w) { this.w = w; this.h = w; } // keeps it square
void setHeight(int h) { this.w = h; this.h = h; }
}Now this test fails:
void test(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
assertEquals(20, r.area()); // a Square gives 16
}A square is a rectangle in geometry and is not a substitutable Rectangle in code, because the parent's methods promise independent width and height.