Topic: Evolution of Programming – From Machine Language to OOP


System Design Journey – Day 2
📆 13 May|2025
Why Do We Need OOP? A Journey Through Programming History
To understand the need for Object-Oriented Programming (OOP), we must first walk through the evolution of programming languages—starting from Machine Language, moving to Assembly, then to Procedural Programming, and finally arriving at OOP.
🧠 1. Machine Language (1st Generation Language)
How it worked: Code was written using only binary digits—0s and 1s.
Example: 000100111
– this could represent an instruction like “add two numbers.”
Interaction: Direct communication with the CPU.
Limitations:
Prone to errors
Tedious and hard to debug
Not scalable
Not human-readable
Hardware-dependent
⚙️ 2. Assembly Language (2nd Generation Language)
Improvement: Introduced English-like mnemonics.
Example: MOV A, 61H
(moves hexadecimal value 61 into register A)
Benefits:
Easier to understand than binary
Slightly more readable and manageable
Limitations:
Still tightly coupled with hardware
Code had to be rewritten for different CPUs
Executed line-by-line – error-prone and not scalable
Lacked loops, functions, and methods
📘 3. Procedural Programming (3rd Generation Language)
Breakthrough: Introduced functions, loops, blocks, switch statements.
Example Language: C
Analogy: Like following a recipe book—a step-by-step list of instructions.
Strengths:
Suitable for small-scale problem-solving
Enabled partial code reuse
Limitations:
Struggled to model complex real-world scenarios
Separation of functions and data could cause maintenance and security issues
Difficult to manage and scale large systems
🚗 4. Object-Oriented Programming (OOP)
Real-World Modeling: To solve real-world problems (e.g., apps like Uber, Paytm, Zomato), we must model software after real-life entities.
Core Idea: In real life, everything is an object with:
Properties (Characteristics)
Methods (Behaviors)
🔧 Example: A Car
🧱 Without OOP (Procedural Way):
string brand;
string model;
bool isEngineOn;
void start() {}
void stop() {}
void gearShift() {}
To allow an owner to drive the car:
void drive(string brand, string model) {
start();
gearShift();
accelerate();
}
But if there's another owner with a different car, you'll need to rewrite variables and functions. This approach is repetitive, hard to maintain, and not scalable.
🎯 How OOP Solves This
In OOP, we model entities using classes and objects.
✅ OOP Approach:
class Car {
public:
string brand;
string model;
bool isEngineOn;
void start() {}
void stop() {}
void gearShift() {}
void accelerate() {}
};
class Owner {
public:
Car car;
void drive() {
car.start();
car.gearShift();
car.accelerate();
}
};
With this structure, you can create multiple owners or cars by instantiating new objects from the same class. This makes code:
Reusable
Scalable
Secure (via encapsulation)
Easier to understand and maintain
✨ Benefits of OOP
Encapsulation: Bundles data with the methods that operate on it
Inheritance: Reuses code from parent classes
Polymorphism: Same method behaves differently based on the object
Abstraction: Hides complex logic, exposing only essential features
🔐 Abstraction (OOP Pillar 1/4)
🔍 What is Abstraction?
Abstraction means hiding internal details and exposing only essential features to the user.
Real-Life Analogy: Driving a Car
You interact with:
Steering Wheel (turn)
Accelerator (speed up)
Brake (stop)
Gear (change speed)
But you don’t need to know:
How the engine combusts fuel
How transmission works
How the braking system operates
Similarly, abstraction in programming hides internal complexity and exposes only the necessary interfaces.
🖥️ Abstraction in C++ (Using Virtual Functions)
#include <iostream>
using namespace std;
class Car {
public:
virtual void start() = 0;
virtual void accelerate() = 0;
virtual void brake() = 0;
};
class SportsCar : public Car {
public:
void start() override {
cout << "SportsCar is starting with a button press...\n";
}
void accelerate() override {
cout << "SportsCar is accelerating quickly!\n";
}
void brake() override {
cout << "SportsCar is braking smoothly.\n";
}
};
int main() {
Car* myCar = new SportsCar();
myCar->start();
myCar->accelerate();
myCar->brake();
delete myCar;
return 0;
}
Explanation:
Car
is an abstract class with virtual methods.SportsCar
implements those methods.main()
interacts only with the abstract interface.
This is abstraction—just like driving a car without needing to understand engine mechanics.
💡 Real-Life Examples of Abstraction:
TV Remote: Press buttons without knowing the circuitry.
Phone Call: Dial a number, unaware of signal routing.
Google Maps: Get directions, unaware of satellite calculations.
📌 Abstraction in High-Level Languages
Languages like C++, Java, and Kotlin provide abstraction:
if
,switch
,for
– you don’t handle CPU instructions.try-catch
– exception logic handled internally.std::vector
– handles memory management for you.
Even a simple line:
int a = 5;
uses abstraction—you don’t manually allocate memory.
✅ Conclusion – Abstraction
Hides unnecessary details
Shows only essential features
Reduces complexity
Makes code easier to maintain, reuse, and extend
Just like a car's interface, OOP allows you to interact through clean methods while hiding the engine’s complexity.
🧱 Encapsulation (OOP Pillar 2/4)
🔍 What is Encapsulation?
Encapsulation is the process of bundling data (characteristics) and functions (behaviors) into a single unit—a class.
Analogy: Like a medicine capsule containing different ingredients, a class wraps variables and methods together.
🚗 Real-Life Analogy (Car)
A car has:
Characteristics: brand, speed, fuel, odometer
Behaviors: start(), stop(), accelerate(), brake()
Some data (like the odometer or engine temperature) should not be changed externally—this is where data security is important.
❗ Abstraction vs Encapsulation
Concept | Focuses On | Analogy |
Abstraction | Hiding unnecessary details | You don’t need to know how the engine works |
Encapsulation | Bundling & securing data | You cannot directly modify the car’s odometer |
🔐 Access Modifiers in C++
public
: accessible outside the classprivate
: not accessible outside the classprotected
: accessible within the class and its derived classes
👨💻 C++ Example – Encapsulation
#include <iostream>
using namespace std;
class Car {
private:
int odometer;
bool engineOn;
public:
string brand;
string model;
Car(string b, string m) {
brand = b;
model = m;
odometer = 0;
engineOn = false;
}
void start() {
engineOn = true;
cout << brand << " " << model << " started.\n";
}
void drive(int distance) {
if (!engineOn) {
cout << "Start the car first!\n";
return;
}
odometer += distance;
cout << "Driving " << distance << " km...\n";
}
int getOdometer() {
return odometer;
}
void stop() {
engineOn = false;
cout << "Car stopped.\n";
}
};
🧾 Usage:
int main() {
Car myCar("Toyota", "Camry");
myCar.start();
myCar.drive(50);
myCar.stop();
// myCar.odometer = 1000; ❌ — Not allowed
cout << "Total distance driven: " << myCar.getOdometer() << " km\n";
return 0;
}
🔎 Explanation:
odometer
andengineOn
are private—cannot be modified directly.Only accessible via controlled methods like
getOdometer()
anddrive()
.
✅ Conclusion – Encapsulation
Groups data and behaviors together
Controls access using access modifiers
Secures sensitive information
Prevents unintended data manipulation
Think of it as the hood of a car—everything is bundled and protected inside.
Subscribe to my newsletter
Read articles from Aparna Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Aparna Singh
Aparna Singh
👨💻 Full-stack web developer | Learning System Design 🚀 Passionate about building smart solutions & solving real-world problems 🧠 Love exploring EdTech, AI, and smart city innovations 🛠️ Hackathon enthusiast | Enjoy working on impactful, fast-paced projects 📚 Documenting my dev journey through blogs & code snippets 🎯 Aiming to become a confident tech leader & open-source contributor 🤝 Let’s connect, learn, and grow together in tech!