Skip to main content
Clean Architecture 70 XP · 7 min

LSP: The Liskov Substitution Principle

Subtypes must be substitutable for their base types — LSP violations at the architectural level force expensive special-case logic into the design.

Showing
Ad (728×90)

Why this matters

Barbara Liskov's substitution rule states that if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering the correctness of the program. This is not just a class-level rule — Martin shows how LSP violations ripple up to the architectural level.

The architecture example involves a taxi dispatch service: if two taxi companies expose slightly different REST interfaces, the dispatch system must add a special case for one of them. That special case is an LSP violation at the service boundary. Similarly, the classic Rectangle/Square trap shows how a subclass that silently changes parent behavior breaks any code that holds a reference to the parent type. The rule: if you must write if isinstance(obj, Subclass) before using it, the substitution principle has been violated and your design will accumulate dispatch logic over time.

The problem

Square extends Rectangle but silently keeps both sides equal — code that widens a Rectangle produces wrong area when passed a Square.

Bad

class Rectangle:
    def __init__(self, w: float, h: float):
        self.width = w
        self.height = h

    def set_width(self, w: float) -> None:  self.width = w
    def set_height(self, h: float) -> None: self.height = h
    def area(self) -> float: return self.width * self.height

class Square(Rectangle):          # LSP violation
    def set_width(self, w: float) -> None:
        self.width = self.height = w   # silently overrides both!

    def set_height(self, h: float) -> None:
        self.width = self.height = h

def make_wider(r: Rectangle) -> None:
    r.set_width(r.height * 2)
    # Postcondition: width == height * 2 — FALSE when r is a Square
class Rectangle {
  constructor(protected width: number, protected height: number) {}
  setWidth(w: number): void  { this.width = w; }
  setHeight(h: number): void { this.height = h; }
  area(): number { return this.width * this.height; }
}

class Square extends Rectangle {      // LSP violation
  setWidth(w: number): void  { this.width = this.height = w; }
  setHeight(h: number): void { this.width = this.height = h; }
}

function makeWider(r: Rectangle): void {
  r.setWidth(r["height"] * 2);
  // Postcondition violated when r is actually a Square
}

The solution

Rectangle and Square both implement a common Shape interface independently — no problematic inheritance relationship between them.

Good

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...

class Rectangle(Shape):
    def __init__(self, w: float, h: float):
        self._width = w
        self._height = h

    def set_width(self, w: float) -> None:  self._width = w
    def set_height(self, h: float) -> None: self._height = h
    def area(self) -> float: return self._width * self._height

class Square(Shape):    # independent — no broken inheritance
    def __init__(self, side: float):
        self._side = side

    def set_side(self, s: float) -> None: self._side = s
    def area(self) -> float: return self._side ** 2
interface Shape {
  area(): number;
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}
  setWidth(w: number): void  { this.width = w; }
  setHeight(h: number): void { this.height = h; }
  area(): number { return this.width * this.height; }
}

class Square implements Shape {   // independent, no broken inheritance
  constructor(private side: number) {}
  setSide(s: number): void { this.side = s; }
  area(): number { return this.side ** 2; }
}

Key takeaway

If you need to check the type of an object before using it, LSP is violated — and your architecture will accumulate expensive special-case dispatch logic every time a new subtype is added.

Done with this lesson?

Mark it complete to earn XP and track your progress.