Phase 3: Design Patterns in Action

Focus on when & why to use patterns, not just memorization. Most LLD problems reduce to Strategy + Factory + Composition!

💡
Pro Tip

The absolute secret to passing LLD (Low-Level Design): The Golden Trio. Most interview problems (Parking Lot, Elevator, TicTacToe, Payment Gateway) can be solved robustly using a combination of the Factory, Strategy, and Observer patterns alongside solid Object Composition.

The Golden Trio Applied: A Parking Lot

Let's build the crucial components of a Parking Lot design using the Golden Trio. A Parking Lot needs to dynamically assign parking spots based on different strategies (Nearest to entrance, VIP first, Random). It also needs to construct different types of tickets (Factory).

1. The Strategy Pattern (Dynamic Algorithms)

Instead of hardcoding `assignSpot` with giant if/else blocks, we abstract the algorithm.
typescript
// The abstraction representing the algorithm
interface IParkingStrategy {
    findSpot(spots: ParkingSpot[], vehicle: Vehicle): ParkingSpot | null;
}

// Concrete Strategy A
class NearestToEntranceStrategy implements IParkingStrategy {
    findSpot(spots: ParkingSpot[], vehicle: Vehicle): ParkingSpot | null {
        // Logic to find spot with lowest distance metric that fits vehicle type
        return spots.find(s => s.isFree && s.size >= vehicle.size) || null;
    }
}

// Concrete Strategy B
class VIPFirstStrategy implements IParkingStrategy {
    findSpot(spots: ParkingSpot[], vehicle: Vehicle): ParkingSpot | null {
        // Look for VIP spots first, then fallback
        return fallbackLogic();
    }
}

2. The Factory Pattern (Scalable Instantiation)

When a car enters, we issue a Ticket. We use a Factory to encapsulate the complex initialization logic.
typescript
class TicketFactory {
    private static idCounter = 1;

    public static generateTicket(vehicle: Vehicle, spot: ParkingSpot): Ticket {
        const ticketId = "TKT-" + this.idCounter++;
        const entryTime = new Date();
        
        // Factory encapsulates complexity
        spot.isFree = false;
        
        return new Ticket(ticketId, vehicle, spot, entryTime);
    }
}

3. Composition (Entry Gate Assembly)

Now we bind it all together at the EntryGate.
typescript
class EntryGate {
    private gateId: string;
    private parkingStrategy: IParkingStrategy; // Dependency Injection

    constructor(id: string, strategy: IParkingStrategy) {
        this.gateId = id;
        this.parkingStrategy = strategy;
    }

    public processVehicleEntry(vehicle: Vehicle, allSpots: ParkingSpot[]): Ticket | null {
        // 1. Ask Strategy for a spot
        const spot = this.parkingStrategy.findSpot(allSpots, vehicle);
        
        if (!spot) {
            console.log("No spots available!");
            return null;
        }

        // 2. Ask Factory for a Ticket
        const ticket = TicketFactory.generateTicket(vehicle, spot);
        return ticket;
    }
}
If the interviewer says: "We now need to change the spot assignment algorithm during rush hour." Because you used the **Strategy Pattern**, you just inject a new `RushHourStrategy` into the `EntryGate`, completely satisfying the **Open-Closed Principle (OCP)**!

🎯

Take the Phase Interview

Solidify your knowledge. We will ask you 5 random highly-technical questions from the Phase 3: Design Patterns in Action bucket. A Staff Engineer AI will strictly evaluate your answers.