Object Oriented Architecture
Designing resilient software using encapsulation, polymorphism, and memory safety.
1. Classes, Objects & Memory Layout
Understanding the blueprint vs. the instance, and how they sit in physical memory.
The Fundamentals
- Class: A logical template (User-defined data type). No memory allocated on definition.
- Object: A physical instance of a class. Occupies memory (Stack or Heap).
- Access Modifiers: Public (Open), Private (Self only), Protected (Self + Kids).
Engineering Deep Dive
- Memory Padding: Compilers add padding bytes to align data members to word boundaries (structure padding) for CPU efficiency.
- Empty Class Size: In C++, an empty class takes 1 byte (not 0) to ensure distinct objects have distinct addresses.
- This Pointer: A hidden const pointer passed to every non-static member function. It points to the invoking object.
class Hero {
// 1 byte (char) + 3 bytes (padding)
char rank;
// 4 bytes (int)
int health;
// Total size = 8 bytes (not 5)
public:
void heal() {
// Compiler converts to: heal(Hero* const this)
this->health = 100;
}
};2. Inheritance Deep Dive
Structuring relationships between classes. 'IS-A' relationship mechanics.
The Fundamentals
- Single: A->B. Multilevel: A->B->C. Hierarchical: A->B, A->C.
- Multiple: A->C, B->C (Supported in C++, not Java/C# classes).
- Hybrid: Combination of above.
Engineering Deep Dive
- The Diamond Problem: If B & C inherit from A, and D inherits from B & C, D has TWO copies of A. Solution: Virtual Inheritance (class B : virtual public A).
- Constructor Order: Parent -> Child. Destructor Order: Child -> Parent (Stack Unwinding).
- Method Hiding: If Child defines a non-virtual function with same name as Parent, Parent's function is hidden (not overridden).
- Composition over Inheritance: Prefer 'HAS-A' (Member variable) over 'IS-A' for flexibility.
class A { public: int x; };
class B : virtual public A { }; // Virtual Base
class C : virtual public A { };
class D : public B, public C { };
// Without virtual: D has B::x and C::x (Ambiguity)
// With virtual: D has single shared instance of A::xFatal Mistakes
- × Non-virtual destructor in Base→ Causes memory leaks. Always make Base destructor virtual.
- × Overriding without 'override'→ Use 'override' keyword to prevent accidental hiding/signature mismatch.
3. Abstract Classes vs Interfaces
Defining contracts vs defining templates.
The Fundamentals
- Abstract Class: Cannot be instantiated. May contain implementation.
- Interface: Pure contract. No state (variables). All methods public abstract (mostly).
- Pure Virtual Function: 'virtual void func() = 0;' makes a class Abstract in C++.
Engineering Deep Dive
- State: Abstract classes can have member variables. Interfaces cannot (only static final constants).
- Constructors: Abstract classes have them (called by child). Interfaces do not.
- Multiple Inheritance: A class can implement multiple Interfaces, but extend only one Abstract class.
- Java 8+: Interfaces can have 'default' methods with implementation.
// Abstract Class
abstract class Animal {
int age; // State allowed
Animal(int age) { this.age = age; } // Constructor allowed
abstract void sound();
void breathe() { ... } // Partial implementation
}
// Interface
interface Flyable {
// int x; // ERROR: Instance variable not allowed
void fly(); // Implicitly public abstract
}4. Static Members & The 'this' Pointer
Class-level vs Object-level scope.
The Fundamentals
- Static Variable: Shared copy across ALL objects. Allocated in Data Segment.
- Static Method: Can only access static data. No 'this' pointer.
- main(): Static because it runs before any object is instantiated.
Engineering Deep Dive
- Initialization Blocks: 'static { ... }' in Java/C# runs once when class loads.
- Local Static: In C++, static inside a function initializes only once and persists between calls.
- Returning *this: Used for method chaining (e.g., obj.setX().setY()).
class Counter {
static int count; // Shared
public:
Counter& add() {
count++;
return *this; // Return reference for chaining
}
static void show() {
// cout << this->count; // ERROR: No 'this'
cout << count;
}
};
int Counter::count = 0; // Definition outside5. Operator Overloading & Friend Functions
Giving special meaning to standard operators (+, -, <<) for user types.
The Fundamentals
- Member Function: Left operand must be the object class.
- Friend Function: Non-member function with access to private data. Needed when Left operand is NOT the class (e.g., cout << obj).
- Restrictions: Cannot overload . :: ?: sizeof
Engineering Deep Dive
- Assignment (=) vs Copy Constructor: Copy Ctor creates new object. Assignment updates existing object.
- Prefix (++i) vs Postfix (i++): Postfix takes a dummy 'int' argument to differentiate.
- Friendship: Not inherited. Not transitive (Friend of Friend is not Friend).
class Complex {
int r, i;
public:
// Member overload (c1 + c2)
Complex operator+(const Complex& other) {
return Complex(r + other.r, i + other.i);
}
// Friend overload (cout << c1)
friend ostream& operator<<(ostream& os, const Complex& c) {
os << c.r << "+i" << c.i;
return os;
}
};6. Exception Handling & Safety
Handling runtime errors gracefully without crashing.
The Fundamentals
- Keywords: try, catch, throw, finally (Java/C#).
- Flow: Execution stops at 'throw', stack unwinds looking for 'catch'.
- Hierarchy: Checked (compile-time) vs Unchecked (runtime) exceptions.
Engineering Deep Dive
- RAII (Resource Acquisition Is Initialization): In C++, local objects in 'try' block are destructed during stack unwinding. Use smart pointers.
- noexcept: C++11 specifier promising function won't throw. Allows compiler optimizations.
- Exception Safety Levels: Basic (no leak), Strong (commit/rollback), No-throw.
void process() {
try {
if (error) throw runtime_error("Fail");
} catch (const exception& e) {
// Stack unwinds, destructors called here
cout << e.what();
} catch (...) {
// Catch all
}
}7. Essential Design Patterns
Standard solutions to common architectural problems.
The Fundamentals
- Singleton: One instance globally.
- Factory: Create objects without specifying exact class.
- Observer: Publish-Subscribe (YouTube subs).
- Strategy: Swap algorithms at runtime (Sort types).
- Decorator: Add behavior dynamically (Coffee toppings).
Engineering Deep Dive
- Thread-Safe Singleton: Double-Checked Locking prevents race conditions during initialization.
- Observer Memory Leak: Lapsed Listener problem if observers aren't deregistered.
// Thread-Safe Singleton
class Singleton {
private static volatile Singleton instance;
private Singleton() {} // Private Ctor
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}Relational Database Theory
Data integrity, normalization, and query optimization engines.
1. SQL Query Execution Order
Why you can't use an alias defined in SELECT inside the WHERE clause.
The Fundamentals
- Lexical Order: SELECT ... FROM ... WHERE ...
- Logical Order: FROM -> JOIN -> WHERE -> GROUP BY -> HAVING -> SELECT -> DISTINCT -> ORDER BY -> LIMIT
Engineering Deep Dive
- Why: The database first finds the data (FROM/JOIN), filters rows (WHERE), groups them (GROUP BY), filters groups (HAVING), then finally computes values (SELECT).
- Implication: You cannot use calculated columns (e.g., 'Salary * 12 AS Annual') in WHERE. You MUST repeat the calculation or use a CTE/Subquery.
-- INCORRECT
SELECT Salary * 12 AS Annual FROM Emp WHERE Annual > 100000; -- Error: Annual unknown
-- CORRECT
SELECT Salary * 12 AS Annual FROM Emp WHERE Salary * 12 > 100000;
-- BETTER (Using HAVING for groups)
SELECT Dept, SUM(Salary) as Total
FROM Emp
GROUP BY Dept
HAVING SUM(Salary) > 500000;2. Joins Deep Dive
Combining tables. The power of Relational Databases.
The Fundamentals
- INNER: Match in both.
- LEFT/RIGHT: All from one side + Matches (NULL if no match).
- FULL OUTER: All from both sides.
- CROSS: Cartesian Product (All x All). Warning: Huge result.
Engineering Deep Dive
- SELF JOIN: Joining a table to itself. Essential for Hierarchy (Manager/Employee).
- Algorithms: Nested Loop (Small tables), Hash Join (Large unsorted), Merge Join (Sorted data).
- Complexity: Hash Join is O(N+M). Nested Loop is O(N*M).
-- Self Join: Find Employees earning more than their Manager
SELECT e.Name
FROM Employee e
JOIN Employee m ON e.ManagerID = m.ID
WHERE e.Salary > m.Salary;3. Subqueries vs Joins vs EXISTS
Nested queries. When to use what.
The Fundamentals
- Scalar Subquery: Returns single value.
- Correlated: Inner query references Outer query. Runs once per row (Slow).
- Non-Correlated: Runs once total.
Engineering Deep Dive
- IN vs EXISTS: 'IN' builds a list of values. 'EXISTS' short-circuits (stops at first match). EXISTS is faster for checking existence in large tables.
- Performance: Joins are usually faster than Correlated Subqueries because the optimizer can use Hash Joins.
-- Correlated Subquery (Often Slow)
SELECT * FROM Emp e1
WHERE Salary > (SELECT AVG(Salary) FROM Emp e2 WHERE e2.DeptID = e1.DeptID);
-- EXISTS (Efficient)
SELECT * FROM Dept d
WHERE EXISTS (SELECT 1 FROM Emp e WHERE e.DeptID = d.ID);4. Transactions, Locks & Concurrency
Ensuring data safety in a multi-user environment.
The Fundamentals
- ACID: Atomicity, Consistency, Isolation, Durability.
- Commands: BEGIN, COMMIT, ROLLBACK, SAVEPOINT.
Engineering Deep Dive
- Pessimistic Locking: Lock row/table before updating. 'SELECT ... FOR UPDATE'. Prevents conflicts but reduces concurrency.
- Optimistic Locking: Check version number on save. If version changed, fail. Good for low-conflict.
- Deadlock: Transaction A waits for B, B waits for A. DB detects and kills one.
BEGIN TRANSACTION;
UPDATE Inventory SET Qty = Qty - 1 WHERE ID = 101;
-- If error occurs or stock < 0
ROLLBACK;
-- Else
COMMIT;5. Database Constraints & Keys
Enforcing rules at the schema level.
The Fundamentals
- Primary Key: Unique + Not Null. Clustered Index.
- Foreign Key: Links tables. Referential Integrity.
- Unique: Unique values, allowed Nulls.
Engineering Deep Dive
- Cascade Actions: ON DELETE CASCADE (Delete parent -> Delete children). SET NULL (Delete parent -> Child FK becomes NULL).
- Check Constraint: 'CHECK (Age >= 18)'. Validates data logic.
- Composite Key: PK made of multiple columns. Used in many-to-many junction tables.
6. DELETE vs TRUNCATE vs DROP
| Feature | DELETE | TRUNCATE |
|---|---|---|
| Type | DML (Data Manipulation) | DDL (Data Definition) |
| Rollback | Yes (Transactional) | No (Usually Auto-commit) |
| Speed | Slow (Logs each row) | Fast (Deallocates pages) |
| Triggers | Fires ON DELETE triggers | Does NOT fire triggers |
| Identity | Continues ID sequence | Resets ID to seed |
7. Views, Materialized Views & Stored Procs
Abstraction layers for SQL.
The Fundamentals
- View: Saved query. Virtual table. No data stored. Simplifies complex joins.
- Materialized View: Physical copy of data. Needs REFRESH. Fast reads, stale data.
- Stored Procedure: Pre-compiled SQL script on server.
Engineering Deep Dive
- Triggers: Auto-execute on Insert/Update/Delete. 'BEFORE' (Validation) vs 'AFTER' (Logging/Audit).
- Stored Proc Benefits: Reduces network traffic, Security (Grant execute only), Pre-compiled execution plan.
8. Performance Tuning
Making queries fly.
The Fundamentals
- EXPLAIN ANALYZE: Shows query execution plan (Scan vs Seek).
- N+1 Problem: Fetching parent, then loop fetching children. Fix with JOIN.
- Select *: Never use in production. Kills covering index.
Engineering Deep Dive
- Index Seek: O(log N). Jumping to specific node.
- Index Scan: O(N). Reading the whole index.
- Clustered Index: Data stored in leaf nodes. Only 1 per table.
- Connection Pooling: Reusing open DB connections to avoid TCP handshake overhead.
Distributed System Design
Scaling from 1 user to 1 billion. CAP theorem, Sharding, and Caching.
1. CAP Theorem & Consistency Patterns
The fundamental trade-off of distributed computing.
The Fundamentals
- Consistency: Every read gets the most recent write.
- Availability: Every request gets a response (no errors).
- Partition Tolerance: System survives network cuts.
- Rule: In a distributed system, P is unavoidable. You choose CP or AP.
Engineering Deep Dive
- CP (Banking): If link cuts, refuse writes to prevent data divergence. (MongoDB default, HBase).
- AP (Social Feed): If link cuts, accept writes. Resolve conflicts later. (Cassandra, DynamoDB).
- Eventual Consistency: The system will become consistent over time (Gossip protocols).
Interview Key
"Never say 'I choose CA'. CA is impossible because networks fail. You must handle Partitions."
3. Caching Strategies
Latency is the new outage. Using RAM to bypass Disk.
The Fundamentals
- Why Cache: RAM is ns (nanoseconds), Disk is ms (milliseconds). 1000x difference.
- Cache Hit/Miss: Finding data in Redis vs going to SQL.
- TTL (Time To Live): Auto-expiry of keys.
Engineering Deep Dive
- Cache-Aside (Lazy): App reads Cache -> Miss -> Read DB -> Write Cache. (Stale window possible).
- Write-Through: App writes Cache + DB sync. (Safe, slow writes).
- Write-Back: App writes Cache -> Cache async updates DB. (Fastest, data loss risk).
- Thundering Herd: Cache expires, 10k users hit DB at once. Fix: Request Coalescing or Jitter TTL.
4. Design Twitter / News Feed
The classic 'Fan-out' problem.
The Fundamentals
- Requirements: Post tweets, Follow users, View timeline.
- Scale: Read-heavy (100:1 Read/Write ratio).
Engineering Deep Dive
- Pull Model (Fan-out on Load): User visits feed -> Query all follows -> Merge & Sort. Simple, but slow for user.
- Push Model (Fan-out on Write): User tweets -> System writes ID to the pre-computed feed of ALL followers.
- Hybrid Approach: Push for normal users. Pull for Celebrities (Justin Bieber tweeting shouldn't trigger 100M writes).
Interview Key
"Start with Pull. Mention the latency issue. Pivot to Push. Mention the celebrity issue. Pivot to Hybrid. This shows seniority."