HT
How Things Work
System Online

OOP: The Four Pillars in Practice

Encapsulation, abstraction, inheritance, and polymorphism — what they actually buy you beyond the textbook definitions, and when each helps or hurts.

How It Works

Object-oriented programming organizes code around objects that bundle state and behavior. Its four pillars are tools for managing complexity: encapsulation protects invariants, abstraction hides detail, inheritance reuses behavior, and polymorphism lets one call serve many types. Used well they enable SOLID and clean architecture; used carelessly (deep inheritance, anemic objects) they create the messes they were meant to prevent.

1
Encapsulation — Protect the Invariants

Bundle state with the methods that change it, and hide that state behind a controlled interface (private fields, getters, methods). Because outside code can't reach in and corrupt the data, the object can guarantee it's always valid — e.g. a balance that can never go negative.

2
Abstraction — Hide the How

Expose a simple contract (what an object does) and hide the messy implementation (how it does it). Callers depend on the interface, not the internals, so the implementation can change freely without breaking them. Abstraction manages complexity by hiding it.

3
Inheritance — Reuse via IS-A

Derive a specialized class from a more general one to reuse and extend its behavior. It's powerful for genuine IS-A relationships, but the most overused pillar: deep hierarchies become rigid and fragile. When in doubt, favor composition (HAS-A) over inheritance.

4
Polymorphism — One Call, Many Forms

The same method call dispatches to different implementations based on the object's actual runtime type. This is what lets you write code against an abstraction and have it 'just work' for every concrete type — eliminating type-checking if/else ladders and enabling the Open/Closed principle.

Key Concepts

📦Encapsulation

Bundling data with behavior and hiding internal state so invariants can't be violated from outside.

🎭Abstraction

Exposing a simple contract while hiding implementation detail — callers depend on 'what', not 'how'.

🧬Inheritance

Specializing a base type to reuse/extend behavior. Best for true IS-A; easy to overuse.

🔀Polymorphism

One interface dispatching to many implementations at runtime — no switching on type.

🧱Composition over Inheritance

Build behavior by combining objects (HAS-A) rather than deep inheritance trees — more flexible, less coupled.

📇Virtual Dispatch

The runtime mechanism (vtable) that selects the correct overridden method for an object's actual type.

Encapsulation + polymorphism
tsx
1// Encapsulation + polymorphism working together.
2abstract class Notification {
3 protected abstract deliver(msg: string): Promise<void>; // hidden HOW
4
5 // public contract — guards the invariant, hides delivery details
6 async send(msg: string) {
7 if (!msg.trim()) throw new Error("empty message"); // invariant
8 await this.deliver(msg); // polymorphic
9 this.audit(msg);
10 }
11 private audit(msg: string) { /* private, encapsulated */ }
12}
13
14class EmailNotification extends Notification {
15 protected async deliver(m: string) { /* SMTP */ }
16}
17class SmsNotification extends Notification {
18 protected async deliver(m: string) { /* Twilio */ }
19}
20
21// One call site, many behaviors — no switch on type:
22for (const n of channels) await n.send("Your order shipped");
💡
Why This Matters

OOP is the dominant paradigm in C#, Java, and TypeScript backends, and the four pillars are foundational interview material. But the deeper value is judgment: knowing that polymorphism beats type-switching, that encapsulation prevents whole classes of bugs, and that composition usually beats inheritance is what separates clean OO design from a tangle of classes.

Common Pitfalls

Anemic objects — public data with no behavior, so invariants live everywhere and nowhere.
Inheritance for code reuse instead of true IS-A — leads to fragile base classes and rigid trees.
Exposing internals via getters/setters for everything, defeating encapsulation.
Leaky abstractions that force callers to know implementation details anyway.
Type-checking with instanceof/switch where polymorphism would dispatch automatically.
Real-World Use Cases

1The Anemic Object That Lost Its Invariants

Scenario

An Order class is just public fields (status, total, items) with no methods. Business rules ('can't ship a cancelled order', 'total must equal sum of items') are scattered across services that mutate the fields directly.

Problem

With no encapsulation, any code can put the Order into an invalid state, and the same rule is re-implemented (inconsistently) in many places. Bugs appear when one path forgets a check.

Solution

Make the fields private and expose intent-revealing methods (order.cancel(), order.addItem()) that enforce the invariants in one place. The object now guarantees its own validity — it's impossible to reach an illegal state from outside.

💡

Takeaway: Encapsulation isn't about getters/setters — it's about putting rules where the data lives so invalid states are unrepresentable. Anemic objects push that responsibility onto everyone else, and it leaks.

2The Fragile Inheritance Tree

Scenario

A reporting system models reports with a 6-level inheritance hierarchy. A change to the base PdfReport breaks three unrelated subclasses, and a new requirement doesn't fit the tree at all.

Problem

Inheritance was used for code reuse, not genuine IS-A relationships. The base class accumulated behavior subclasses didn't all want, creating tight coupling — the 'fragile base class' problem.

Solution

Refactor to composition: extract behaviors (formatting, data source, delivery) into small collaborator objects that reports HAVE, and combine them as needed. New report types mix-and-match collaborators instead of squeezing into a rigid tree.

💡

Takeaway: Reach for inheritance only when a subtype truly IS-A base type and honors its contract. For reuse and flexibility, composition almost always ages better than a deep class hierarchy.

Join the discussion

Comments

?

Loading comments...

Cookie preferences

We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.