Back to the path
Level 0 beginner 20 min #oop#fundamentals

The Four Pillars of OOP

Encapsulation, abstraction, inheritance, polymorphism — the vocabulary every LLD discussion is built on.

Why these four words matter

Every LLD interview, every code review, every “why is this class like this?” conversation runs on the same four-word vocabulary. Get fluent in them and you can name what good design is doing — which is the first step to producing it yourself.

1. Encapsulation — guard your invariants

Encapsulation is bundling data + the methods that operate on it, and hiding the internals so the outside world can only touch them through a controlled surface.

The point isn’t secrecy for its own sake — it’s protecting invariants (rules that must always be true). If a BankAccount exposes account.balance = -500, the invariant “balance ≥ 0” is unenforceable. Make balance private and force all changes through deposit()/withdraw(), and the rule lives in exactly one place.

Rule of thumb: if breaking a field would break a business rule, that field should not be publicly settable.

2. Abstraction — expose the what, hide the how

Abstraction is modeling something by its essential behavior while hiding implementation detail. A List tells you add, get, size — you don’t care if it’s an array or linked nodes underneath.

Encapsulation and abstraction are cousins: encapsulation is the mechanism (private fields, methods), abstraction is the result (a clean conceptual surface).

3. Inheritance — reuse via “is-a” (carefully)

Inheritance lets a subclass acquire fields/behavior from a superclass. SavingsAccount extends BankAccount because a savings account is a bank account.

It’s powerful but the most over-used pillar. Dangers:

  • Tight coupling — the subclass depends on the parent’s internal implementation.
  • Fragile base class — a change in the parent silently breaks children.
  • False “is-a” — people reach for inheritance to reuse code, not to model a real hierarchy.
Common pitfall

The #1 LLD interview red flag is reaching for extends just to reuse code. If the relationship isn’t a true is-a, you want composition. You’ll meet the antidote in the next lesson.

4. Polymorphism — one interface, many behaviors

Polymorphism lets a single interface represent many concrete types, with the right behavior chosen at runtime.

interface PaymentMethod { pay(amount: number): Receipt; }

class CardPayment implements PaymentMethod { pay(a) { /* ... */ } }
class UpiPayment  implements PaymentMethod { pay(a) { /* ... */ } }

function checkout(method: PaymentMethod, amount: number) {
return method.pay(amount);   // doesn't know or care which one
}

checkout is written once and works for every present and future payment method. This is the seed of nearly every design pattern you’ll learn — program to an interface, not an implementation.

Key idea

Program to an interface, not an implementation. Almost every design pattern in Level 3 is just a disciplined application of this one sentence. Tattoo it on your brain.

How they work together

A good design uses all four at once: abstraction decides the interface, encapsulation protects the data behind it, polymorphism lets many types fulfill it, and inheritance (sparingly) shares the truly common parts.

Take the assessment below — the design problem is where this stops being trivia and starts being a skill.

Assessment

Pass mark: 70% on the concept questions unlocks the next lesson.

1. Hiding an object's internal state and exposing behavior only through methods is which pillar?

2. A `PaymentProcessor` interface with `card`, `upi`, and `wallet` implementations that callers use interchangeably is an example of:

3. Which of these are genuine risks of using inheritance carelessly? (select all) (select all)

Design problem 4

Model a `BankAccount` that protects its balance. No caller should set the balance to a negative number directly. Sketch the fields and methods and explain which pillar you're leaning on.