OOP Cheatsheet

Quick reference for OOP principles, SOLID, and design patterns.

OOPClassesSOLIDDesign Patterns
NotesCheatsheet

Four Pillars — Quick Reference

PillarOne-linerKeyword
EncapsulationBundle data + methods; hide internals with access modifiersprivate / #field
InheritanceChild class reuses parent's fields and methods; "is-a" relationshipextends / super()
PolymorphismSame interface, different implementations; method overriding at runtimeoverride / virtual
AbstractionExpose what, hide how; define contracts via abstract classes / interfacesabstract / interface

Encapsulation

class BankAccount {
  #balance = 0                        // private field
  deposit(n) { if (n > 0) this.#balance += n }
  get balance() { return this.#balance }  // controlled read
}
// External code can NEVER do: account.#balance = 999

Inheritance & Polymorphism

class Animal {
  speak() { return 'generic sound' }
}
class Dog extends Animal {
  speak() { return 'woof' }           // overrides parent
}
class Cat extends Animal {
  speak() { return 'meow' }
}

const animals = [new Dog(), new Cat()]
animals.forEach(a => console.log(a.speak()))  // woof, meow
// runtime polymorphism — same .speak() call, different result

Abstraction

// TypeScript abstract class
abstract class Shape {
  abstract area(): number           // subclasses MUST implement
  describe() { return `Area: ${this.area()}` }
}
class Circle extends Shape {
  constructor(private r: number) { super() }
  area() { return Math.PI * this.r ** 2 }
}

// Interface — pure contract
interface Printable {
  print(): void
}
class Report implements Printable {
  print() { console.log('Printing report…') }
}

SOLID Principles

LetterPrincipleRule of thumb
SSingle ResponsibilityOne class = one reason to change
OOpen/ClosedOpen for extension, closed for modification
LLiskov SubstitutionSubclass must behave correctly wherever parent is used
IInterface SegregationMany specific interfaces > one fat interface
DDependency InversionDepend on abstractions (interfaces), not concrete classes
// O — Open/Closed: add new discount types without editing existing code
interface Discount { apply(price: number): number }
class PercentDiscount implements Discount {
  constructor(private pct: number) {}
  apply(p: number) { return p * (1 - this.pct / 100) }
}

// D — Dependency Inversion
class OrderService {
  constructor(private db: Database) {}  // Database is an interface
  // swap MySQL for Postgres without touching OrderService
}

Design Patterns — Quick Reference

PatternCategoryIntentWhen to use
SingletonCreationalExactly one instanceConfig, logger, DB connection pool
FactoryCreationalCreate objects without specifying exact classPlugin systems, notification types
ObserverBehaviouralOne-to-many event notificationEvent systems, UI state, pub/sub
StrategyBehaviouralSwap algorithms at runtimeSorting, payment processors, compression
DecoratorStructuralAdd behaviour by wrappingLogging middleware, caching layers
AdapterStructuralMake incompatible interfaces work togetherIntegrating third-party libraries

Singleton & Observer

// Singleton
class Config {
  static #instance
  static getInstance() {
    if (!Config.#instance) Config.#instance = new Config()
    return Config.#instance
  }
}

// Observer
class EventEmitter {
  #handlers = {}
  on(event, fn) { (this.#handlers[event] ??= []).push(fn) }
  emit(event, data) { (this.#handlers[event] ?? []).forEach(fn => fn(data)) }
}

Composition vs Inheritance

// Prefer composition ("has-a") over deep inheritance ("is-a")
// BAD deep chain: Vehicle → Car → ElectricCar → AutopilotCar
// GOOD composition:
class Car {
  constructor(
    private engine: Engine,       // composed behaviour
    private autopilot: Autopilot
  ) {}
  drive() { this.engine.start(); this.autopilot.navigate() }
}

// Rule: if you need more than 2 levels of inheritance, use composition

Key Rules

  • Encapsulate what varies — isolate the parts that change from the parts that stay the same.
  • Program to interfaces, not implementations (Dependency Inversion).
  • Favour composition over inheritance — avoids fragile base-class problem.
  • Tell, don't ask — objects should do things, not expose internal state for callers to act on.
  • SOLID violations show up as: classes that are hard to test, code that breaks when you add a feature, copy-pasted logic across subclasses.