design·lab

L · SOLID

Liskov Substitution

A subtype must be usable anywhere its base type is expected — without breaking the caller's assumptions.

✗ Problem

Square extends Rectangle breaks callers

A Square forces width and height to move together. Any code written against Rectangle's contract can now silently misbehave.

class Square extends Rectangle {
  setWidth(s)  { this.w = this.h = s; }
  setHeight(s) { this.w = this.h = s; }
}

function grow(r) {
  r.setWidth(5);
  r.setHeight(4);
  return r.area();
}

grow(new Rectangle()) // → 20 ✓
grow(new Square())    // → 16 ✗ side got forced
Rectangle
w, h vary independently
↑ extends
Square
setWidth() also sets h
A Square is NOT substitutable for a Rectangle — same grow() call, two different answers. LSP violated.
✓ Refactor

Stop forcing an is-a that isn't true

Introduce a Shape abstraction. Rectangle and Square each implement it independently — siblings, not parent and child.

interface Shape { area(): number }

class Rectangle implements Shape {
  constructor(w, h) { this.w = w; this.h = h; }
  area() { return this.w * this.h; }
}

class Square implements Shape {
  constructor(side) { this.side = side; }
  area() { return this.side ** 2; }
}
Shape
interface · area()
Rectangle
w, h
Square
one side
✓ See it live

Run grow() against each subtype

Same function, same two calls — setWidth(5) then setHeight(4). Pick which subtype gets passed in.

function grow(r) {
  r.setWidth(5);
  r.setHeight(4);
  return r.area();
}
?
passed to grow(r)
area = —

Pick a subtype to pass into grow(r).

✓ Takeaway

Substitutable means the contract holds

  • Preconditions a subtype requires must be no stronger than the base's.
  • Postconditions a subtype guarantees must be no weaker than the base's.
  • Invariants of the base (like "width and height vary independently") must be preserved.
  • Fix: model reality correctly, or favor composition, instead of forcing inheritance onto a relationship that isn't truly is-a.
🎯 Related: a Proxy must be substitutable for the real subject it stands in for — same idea, applied to delegation.