Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Design Principles  ›  Capstone Project

SOLID, Part 2 — Liskov, Interface Segregation and Dependency Inversion

SOLID 18 minSubtypes must keep their parent's promises, interfaces should be small and client-shaped, and high-level policy should depend on abstractions rather than on concrete details.

SOLID, Part 2 — Liskov, Interface Segregation and Dependency Inversion

Shelfwise's dispatch job loops over every product in paid orders and calls product.ship(). The night the first e-book sold, the job crashed: EBook.ship() raised NotImplementedError. The last three SOLID letters explain why that happened and how to design so it cannot.

The rule

Keep the parent's promises, keep interfaces small, and depend on abstractions.

L — Liskov Substitution Principle (LSP). Anywhere code uses a parent type, any subtype must work without surprises. A subclass may do its job differently, but it may not refuse the job, demand more from callers, or give back less than the parent promised. EBook.ship() raising an error breaks every caller that trusted Product.ship().

I — Interface Segregation Principle (ISP). No class should be forced to depend on methods it does not use. The e-book crash has a root cause: Product promised ship() and download_link() to everyone. Split the fat interface:

class Product(ABC):
    @abstractmethod
    def price(self): ...

class Shippable(ABC):
    @abstractmethod
    def ship(self, address): ...

class Downloadable(ABC):
    @abstractmethod
    def download_link(self): ...

class Book(Product, Shippable): ...
class EBook(Product, Downloadable): ...

The dispatch job now asks for Shippable things only, and an e-book is never handed to it.

D — Dependency Inversion Principle (DIP). High-level policy (checkout rules) should not depend on low-level details (one payment provider's client). Both should depend on an abstraction.

class Checkout:
    def __init__(self, gateway: PaymentGateway):   # any gateway
        self.gateway = gateway

Before, Checkout created AcmePayClient() inside itself, so changing provider meant editing checkout. Now the detail plugs into an abstraction that checkout owns, and a test can pass in a FakeGateway. Passing the dependency in like this is called dependency injection.

The shortcut

Run three quick checks on any class:

  • L: Would any subclass surprise a caller of the parent? Look for NotImplementedError, extra preconditions, or weaker results.
  • I: Does any implementer leave a method empty or raising? The interface is too fat.
  • D: Does high-level code call a constructor of a concrete detail (AcmePayClient())? Inject an abstraction instead.
Auditing the dispatch jobStep 1 of 4
  1. Liskov check

    EBook.ship() raises. A caller of Product.ship() gets a surprise. Violation.

The trap is fixing LSP with a type check (if not isinstance(p, EBook): p.ship()). That hides the symptom and brings back the ladder that polymorphism removed.

Drills

Answer fast — trust the rule.

quick drill

FreeSample(Book) overrides price() to raise an error, because samples are not sold. Which principle does this break?

quick drill

A Printer interface has print(), scan() and fax(), and the cheap printer's fax() does nothing. What does ISP suggest?

quick drill

Which constructor follows Dependency Inversion?

quick drill

Why does DIP make Shelfwise's checkout easier to test?

Remember

  • Keep the parent's promises (L), keep interfaces small (I), depend on abstractions (D).
  • Checks: surprising subclasses, empty or raising methods, policy code constructing concrete details.
  • Trap: patching LSP with a type check instead of fixing the interface.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainFix the Product interface

In Shelfwise, the Product base class has ship(), download_link() and price(). EBook.ship() raises NotImplementedError, Book.download_link() returns None, and Checkout creates an AcmePayClient directly inside its constructor. Name the three SOLID principles broken here and fix each one.