Design principles SOLID

SOLID

Five principles that keep classes easy to change, shown on one growing parking lot.

  • 15 min read
  • 7 chapters
  • Lesson 1 of 4

What you will learn

  1. 01Why principles at all
  2. 02S: Single responsibility
  3. 03O: Open for extension, closed for modification
  4. 04L: Liskov substitution
  5. 05I: Interface segregation
  6. 06D: Dependency inversion
  7. 07Using SOLID with judgment
01

Why principles at all

Code is read and changed far more often than it is written. SOLID is a set of five guidelines that make change cheap by keeping each class focused, extensible, substitutable, narrow and loosely wired. They are guides, not laws. We will watch one design improve as we apply each.

usesis ais ais aParking lotLLDcoordinates onlyPricingRuleREQinterfaceCar ruleCHECK20 per hourBike ruleCHECK10 per hourScooter ruleCHECKnew class, no edits

Swipe sideways to pan · drag a box to move it

100%
One class with one job: coordinate the lot.
02

S: Single responsibility

A class should have one reason to change. Here is a class with three.

class ParkingLot {
  fee(t: Ticket, now: number) { /* pricing rules */ }
  print(t: Ticket) { /* receipt layout */ }
  save(t: Ticket) { /* database calls */ }
}

A pricing change, a new receipt format and a new database all force edits to the same file, and each can break the others. Split by reason to change: a PricingPolicy, a ReceiptPrinter, a TicketRepository, and let ParkingLot coordinate them.

03

O: Open for extension, closed for modification

You should add behavior by adding code, not by editing tested code. Replace the if (kind === ...) chain with an abstraction.

interface PricingRule { fee(minutes: number): number }
class CarRule implements PricingRule { fee(m: number) { return Math.ceil(m / 60) * 20 } }
class BikeRule implements PricingRule { fee(m: number) { return Math.ceil(m / 60) * 10 } }

A scooter rule is a new class. Nothing existing changes, so nothing existing can break.

usesis ais ais aParking lotLLDcoordinates onlyPricingRuleREQinterfaceCar ruleCHECK20 per hourBike ruleCHECK10 per hourScooter ruleCHECKnew class, no edits

Swipe sideways to pan · drag a box to move it

100%
It depends on an abstraction, not on each rule.
04

L: Liskov substitution

Anywhere the base type is used, any subtype must work without surprises. A subtype must honor the promises of its parent: accept at least what the parent accepts, and deliver at least what the parent delivers.

The classic violation is a ReadOnlyLot that extends ParkingLot but throws when enter is called. Code written against ParkingLot now crashes. If a subtype must weaken a promise, it should not be a subtype. Model the difference with separate interfaces instead.

05

I: Interface segregation

No client should depend on methods it does not use. One fat interface forces every implementer to supply everything and every caller to know about everything.

interface Reader { find(id: string): Ticket | undefined }
interface Writer { save(t: Ticket): void }

A reporting screen needs only Reader. A test double for it has one method to fake, not ten.

06

D: Dependency inversion

High level policy should not depend on low level detail. Both should depend on an abstraction. Instead of creating its own database, the lot receives something that satisfies an interface.

class ParkingLot {
  constructor(private rules: PricingRule, private store: Writer & Reader) {}
}

Now the lot can be tested with an in-memory store and run in production with a real one, with no change to its code. Handing dependencies in from outside is called dependency injection.

usesis ais ais aParking lotLLDcoordinates onlyPricingRuleREQinterfaceCar ruleCHECK20 per hourBike ruleCHECK10 per hourScooter ruleCHECKnew class, no edits

Swipe sideways to pan · drag a box to move it

100%
A new rule is a new class. Nothing old is edited.
07

Using SOLID with judgment

Every principle adds types and indirection. Apply them where you feel change happening, not on day one for everything. A good test: when requirements change, how many files must you edit? If the answer is usually one, the design is working. If it is usually five, look for the principle that was skipped.