Phase 1: OOP Foundations

Master Encapsulation, Abstraction, Inheritance, and Polymorphism. Learn clean class responsibilities.

Low-Level Design (LLD) interviews demand more than just textbook definitions. They require you to write clean, maintainable, and robust code. Let us dive deep into the real-world application of OOP.

1. Encapsulation: The Bank Account

Encapsulation is not just about making fields private. It is about protecting the **invariants** of your object. A bank account balance should never be directly modified. It must go through controlled logic (deposits, withdrawals) that enforces rules like overdraft limits or thread safety.
typescript
class BankAccount {
    // Hidden from the outside world.
    private balance: number;
    private readonly accountNumber: string;
    private readonly overdraftLimit: number;

    constructor(accountNumber: string, initialBalance: number = 0, overdraftLimit: number = 0) {
        if (initialBalance < 0) throw new Error("Initial balance cannot be negative.");
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
        this.overdraftLimit = overdraftLimit;
    }

    // Controlled public behavior
    public deposit(amount: number): void {
        if (amount <= 0) throw new Error("Deposit must be positive.");
        this.balance += amount;
    }

    public withdraw(amount: number): void {
        if (amount <= 0) throw new Error("Withdrawal must be positive.");
        if (this.balance - amount < -this.overdraftLimit) {
            throw new Error("Insufficient funds. Overdraft limit exceeded.");
        }
        this.balance -= amount;
    }

    public getBalance(): number {
        return this.balance;
    }
}

2. Composition over Inheritance: The Car & Engine

Inheritance implies an "IS-A" relationship, which creates tight coupling and fragility. Composition implies a "HAS-A" relationship, offering flexibility. Do not create `ElectricCar` and `GasCar` subclasses. Instead, inject an `IEngine` into a generic `Car`. This is Dependency Injection at work.
typescript
// The Abstraction Contract
interface IEngine {
    start(): void;
    stop(): void;
    getFuelType(): string;
}

// Concrete Implementations
class V8Engine implements IEngine {
    start(): void { console.log("V8 roaring to life!"); }
    stop(): void { console.log("V8 stopped."); }
    getFuelType(): string { return "Petrol"; }
}

class ElectricMotor implements IEngine {
    start(): void { console.log("Electric motor humming silently."); }
    stop(): void { console.log("Power disconnected."); }
    getFuelType(): string { return "Electric"; }
}

// Composition: The Car "has" an engine.
class Car {
    private engine: IEngine; // Depends on abstraction!

    constructor(engine: IEngine) {
        this.engine = engine;
    }

    public turnOn(): void {
        console.log("Starting the car...");
        this.engine.start();
    }
}
💡
Pro Tip

If you use Inheritance, what happens when you build a Hybrid Car? Do you inherit from both? Do you duplicate code? Composition completely bypasses the fragile base class problem.

3. Abstraction & Interfaces: The Printer Queue

Abstraction separates *what* a system does from *how* it does it. In LLD, always define the boundaries of your system using Interfaces before writing actual implementations.
typescript
interface IPrinterTask {
    getDocumentName(): string;
    getPageCount(): number;
}

interface IPrinterNode {
    print(task: IPrinterTask): boolean;
    getInkLevel(): number;
    getStatus(): 'IDLE' | 'PRINTING' | 'ERROR';
}

class LaserPrinter implements IPrinterNode {
    private inkLevel = 100;
    private status: 'IDLE' | 'PRINTING' | 'ERROR' = 'IDLE';

    public print(task: IPrinterTask): boolean {
        if (this.inkLevel < task.getPageCount() * 0.1) {
            this.status = 'ERROR';
            return false;
        }
        this.status = 'PRINTING';
        this.inkLevel -= task.getPageCount() * 0.1;
        // Print logic...
        this.status = 'IDLE';
        return true;
    }

    public getInkLevel(): number { return this.inkLevel; }
    public getStatus() { return this.status; }
}

🎯

Take the Phase Interview

Solidify your knowledge. We will ask you 5 random highly-technical questions from the Phase 1: OOP Foundations bucket. A Staff Engineer AI will strictly evaluate your answers.