Deepak Singh
3 min read · 13 hours ago
Encapsulation, inheritance, polymorphism — we had them long before OOP.
What OOP really did was add discipline, structure, and accessibility.
And that’s exactly why it changed everything.
The Big Misconception
When people talk about Object-Oriented Programming (OOP), they often ask:
“But didn’t OOP give us encapsulation, inheritance, and polymorphism?”
The truth: No, it didn’t.
All three concepts existed before OOP.
What OOP brought was a way to enforce constraints and discipline around how we use them.
Surprisingly, that’s what made OOP revolutionary.
Let’s break it down.
Encapsulation
Encapsulation means hiding data and exposing behavior through controlled interfaces.
But hiding from whom? And why?
Encapsulation in C
Long before OOP, C programmers used header files (.h
) and implementation files (.c
) to achieve encapsulation.
// mathlib.h
#ifndef MATHLIB_H
#define MATHLIB_H
int add(int a, int b);
#endif
// mathlib.c
#include "mathlib.h"
int add(int a, int b) {
return a + b;
}
// main.c
#include <stdio.h>
#include "mathlib.h"
int main() {
printf("%d\n", add(3, 4)); // Output: 7
return 0;
}
Here, the caller knows a function exists but not how it works.
The same applies to structs: we can declare them without exposing internals, forcing access through functions.
This abstraction might feel like “hiding a book from ourselves” — but it’s intentional.
By forcing ourselves to interact through controlled functions, we reduce coupling and make future changes safe.
What OOP Added
OOP gave us keywords like private
, protected
, and public
.
class Account {
private:
double balance; // hidden
public:
void deposit(double amount) { balance += amount; }
double getBalance() { return balance; }
};
This made encapsulation easier to apply in a single file, even though compilers still know about structure layouts.
So, OOP didn’t invent encapsulation — it just made it mainstream and practical.
Inheritance
Inheritance means reusing code by deriving one type from another.
But guess what? Programmers were already doing it in C.
Inheritance in C
typedef struct {
int x;
int y;
} Point;
typedef struct {
Point base; // reuse Point
char name[50];
} NamedPoint;
By embedding one struct inside another, C developers could simulate inheritance.
But it only worked for single inheritance, and managing it got messy.
What OOP Added
class Point {
public:
int x, y;
};
class NamedPoint : public Point {
public:
std::string name;
};
OOP gave us first-class syntax.
Clean, readable, and no casting tricks required.
So again, inheritance wasn’t new.
OOP just made it simple and expressive.
Polymorphism
Polymorphism means the same function name can behave differently depending on context.
In OOP
class Shape {
public:
virtual void draw() = 0;
};
class Circle : public Shape {
public:
void draw() override { std::cout << "Drawing Circle\n"; }
};
class Square : public Shape {
public:
void draw() override { std::cout << "Drawing Square\n"; }
};
Different objects (like Circle
or Square
) can implement the same function (e.g., draw
) in different ways.
Polymorphism in C
Before OOP, C programmers used function pointers to achieve polymorphism.
#include <stdio.h>
typedef struct {
void (*read)();
} Device;
void readFromConsole() { printf("Reading from console\n"); }
void readFromFile() { printf("Reading from file\n"); }
int main() {
Device console = { readFromConsole };
Device file = { readFromFile };
console.read(); // Reading from console
file.read(); // Reading from file
}
That worked, but it was clunky and risky.
OOP gave us a safer and cleaner way.
Why Polymorphism Changed Everything
Among the three pillars, polymorphism was the game-changer.
Imagine plugging a new device into your PC.
Without polymorphism, you’d need to rewrite functions for each new device.
With polymorphism, as long as the device implements the same interface, your existing code just works.
Even more importantly, polymorphism enables Dependency Inversion.
Higher-level code no longer depends on low-level details.
Instead, both depend on shared contracts (interfaces).
This makes systems:
- More maintainable
- Easier to extend
- Loosely coupled
That’s real power.
Final Thoughts
OOP didn’t invent encapsulation, inheritance, or polymorphism.
Those ideas existed long before.
But OOP gave us discipline, structure, and accessibility — making these ideas practical for everyday programming.
And that’s the real lesson:
Building great software isn’t always about inventing something new.
Sometimes, it’s about applying the right constraints to create powerful systems.
References
Martin, R. C. (2017). Clean Architecture: A Craftsman’s Guide to Software Structure and Design. Prentice Hall.
Top comments (0)