Skip to content

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.

java
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.

java
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:

java
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.