LLD OOP & Patterns
AI Learning Mentor
Generative insights & diagnostic help
OOP Core Principles: Beyond Basics
Object-Oriented Programming (OOP) is the core paradigm of LLD. It maps complex systems into cohesive, interactive objects:
- Encapsulation: Binding the data (state) and methods (behavior) together, restricting direct outside access. Prevents illegal state modifications.
- Abstraction: Hiding internal complexities and exposing only essential interfaces. Focuses on **what** an object does, rather than **how**.
- Inheritance: Reusing code via 'is-a' relationships (e.g. `SavingsAccount` inherits from `BankAccount`). Keep hierarchies shallow! Prefer Composition.
- Polymorphism: The ability of different objects to respond to the same interface call differently at runtime (method overriding).
UML Class Relationships & Lifecycles
Designing clean systems requires establishing precise class relationships. UML outlines three core boundaries:
- 1. Association (Use-a relationship): A loose connection. Objects have independent lifecycles and merely know about each other (e.g., A `Teacher` can teach multiple `Students`, and vice-versa).
class Teacher { private $students = []; } - 2. Aggregation (Has-a relationship): A weak whole-part connection. The child object can exist independently of the parent container (e.g. A `Department` has `Professors`. If the department is deleted, the professors still exist).
class Department { private $professors = []; public function addProfessor(Professor $p) { $this->professors[] = $p; } // Injected! } - 3. Composition (Part-of relationship): A strong, strict whole-part connection. The child cannot exist without the parent. The parent manages the child's instantiation and destruction (e.g., A `House` has `Rooms`. If the house is demolished, the rooms cease to exist).
class House { private $rooms = []; public function __construct() { $this->rooms[] = new Room("Kitchen"); // Created internally! } }
The SOLID Principles Explained
SDE-2 interviews strictly audit your application of the **SOLID Principles** to write extensible, maintainable code:
Concurrency in LLD: Core Primitives
Modern SDE-2 interviews focus heavily on concurrent access safety. Be prepared to master these primitives:
- synchronized Keyword: Restricts access to a method or block, allowing only **one thread** to execute it at a time by securing an intrinsic lock.
- volatile Keyword: Guarantees that variables are read directly from and written to **main memory**, bypassing CPU caches. Prevents thread-visibility problems.
- ReadWriteLock & ReentrantLock: Allows high concurrent reads (`readLock()`) while locking exclusively for writes (`writeLock()`). Highly customizable compared to `synchronized`.
- CountDownLatch: A synchronizer that allows one or more threads to wait until a set of operations being performed in other threads completes (countdown reaches zero).
- CyclicBarrier: A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point before continuing.
Thread-safe Concurrency: The Producer-Consumer Pattern
The **Producer-Consumer** pattern coordinates threads sharing a finite buffer queue, utilizing thread locks, `wait()`, and `notifyAll()` to prevent race conditions and buffer over/underflows:
class SharedQueue {
private LinkedList queue = new LinkedList();
private int capacity = 10;
public synchronized void produce(int item) throws InterruptedException {
while (queue.size() == capacity) {
wait(); // Buffer full - producer waits!
}
queue.add(item);
notifyAll(); // Notify consumers that data is ready
}
public synchronized int consume() throws InterruptedException {
while (queue.size() == 0) {
wait(); // Buffer empty - consumer waits!
}
int item = queue.removeFirst();
notifyAll(); // Notify producers that space is cleared
return item;
}
} Crucial Design Patterns Matrix
Design patterns are standard, reusable templates to solve recurring LLD bottlenecks. Master these primary categories:
- Creational (Instantiations):
• **Singleton**: Ensures only one instance exists (e.g., Thread-safe double-checked lock).
• **Factory Method**: Delegates object instantiation to subclasses.
• **Builder**: Step-by-step construction of complex objects. - Structural (Composition):
• **Adapter**: Converts incompatible interfaces so they can collaborate.
• **Decorator**: Dynamically adds wrapping behaviors to objects without modifying original code.
• **Facade**: Unified, simplified entry face to complex subsystems. - Behavioral (Algorithms & Roles):
• **Strategy**: Defines a family of interchangeable algorithms, selected at runtime.
• **Observer**: Triggers dynamic notifications to subscriber objects when states change.
• **State**: Alter object behavior dynamically when its internal state changes.