The Four Pillars, Revisited for Interviews
You'll learn to
- -Explain encapsulation, abstraction, inheritance, and polymorphism with the nuance an interviewer expects, not just textbook definitions
- -Spot when a candidate design leans on inheritance or getters/setters in a way that actually violates encapsulation
Every CS student can recite encapsulation, abstraction, inheritance, and polymorphism. What separates a strong LLD candidate is applying them with judgment instead of reciting them as trivia - because it is entirely possible to write code that technically has all four pillars and is still a bad design.
Encapsulation Is About Invariants, Not Just Private Fields
Slapping "private" on every field and generating a getter and setter for each one is not encapsulation - it is just a longer way to expose mutable state. Real encapsulation means a class protects its own invariants: an Account class should not let its balance go negative just because it exposed a setBalance() method. If every field has a public setter, external code can put the object into an invalid state, and the class has encapsulated nothing.
class Account:
def __init__(self, balance: float):
self._balance = balance
def withdraw(self, amount: float) -> None:
if amount > self._balance:
raise ValueError("Insufficient funds")
self._balance -= amount
def balance(self) -> float:
return self._balance
# No set_balance() - the only way to change balance is through
# a method that enforces the "never negative" invariant.Abstraction: Hiding Complexity Behind the Right Contract
Abstraction means callers depend on what a class does, not how it does it. A PaymentProcessor.charge(amount) method is an abstraction over card networks, retries, and currency conversion - the caller never needs to know any of that. The interview-relevant nuance: a good abstraction is defined by what the caller actually needs, not by anticipating every possible future need. Over-abstracting (adding parameters and hooks nobody asked for) is its own design smell.
Inheritance and Polymorphism: Substitutability Is the Point
Inheritance is not primarily a code-reuse tool - that is a side effect. Its actual purpose is enabling polymorphism: code written against a base type should work correctly with any subtype, without knowing which one it got. When an interviewer asks you to design a payment system with CreditCard, DebitCard, and Wallet, the win is not that the three classes share code - it is that the checkout flow can call process(payment) once and work for all three, with no type-checking branch anywhere.
A quick sanity check for any inheritance relationship: can you truthfully say "a [Subclass] is a [Superclass]"? "A DebitCard is a Payment" holds up. "A Square is a Rectangle" does not, mathematically, hold up the way most people assume - more on that in the Liskov Substitution chapter.
Why does a class with a public getter and setter for every private field not count as good encapsulation?
"It does count - all the fields are private, so the data is protected."
"Making fields private only to expose an unrestricted setter for each one doesn't protect anything - external code can still put the object into any state, including invalid ones. Real encapsulation means the class's methods enforce its invariants, so it's impossible to construct an invalid instance through the public interface."
What makes encapsulation more than just "making fields private"?