DEV Community

Cover image for Basic Principles of Object-Oriented Programming (OOP)
Ahad Ali
Ahad Ali

Posted on • Edited on

4

Basic Principles of Object-Oriented Programming (OOP)

OOP

Object-Oriented Programming (OOP) is a programming paradigm (way of writing code) that uses objects and classes to model real-world entities.

It’s widely adopted in modern software development due to its ability to create clean, reusable, and scalable code.

Learning OOP allows us to structure code in a way that mirrors real-world systems, making it easier to design and maintain complex applications.


Benefits of OOP

  • Modularity: Organizes code into self-contained units (objects) for easier understanding and maintenance.
  • Reusability: Once a class is written, it can be reused in different parts of the program or across projects.
  • Scalability: New features and objects can be easily added as the codebase grows.
  • Maintainability: Changes are easier to manage, and debugging becomes more efficient.
  • Real-world Modeling: Models real-world entities, making the code more intuitive and relatable.
  • Parallel Development: Enables team members to work on different objects or classes simultaneously.

In this article, I'll break down core OOP concepts with easy-to-understand code examples.


1. Classes & Objects

A class is like a blueprint or a recipe for creating objects. It defines what data and behavior the objects will have. An object is an instance of a class, created using the class definition.

🧠 Think of a class as a car design, and an object as an actual car made using that design.

Here's an example of a class definition in C++:

class Car {
public:
    string brand;
    int year;

    void displayInfo() {
        cout << "Brand: " << brand << ", Year: " << year << endl;
    }
};
Enter fullscreen mode Exit fullscreen mode

In this example, Car is a class - a blueprint that defines the properties and behavior of the class.

  • The class itself doesn’t hold any real data. It just outlines what an object of this type will look like and how it will behave.
Car toyotaCar;
toyotaCar.brand = "Toyota";
toyotaCar.year = 2023;
toyotaCar.displayInfo();

Car hondaCar;
hondaCar.brand = "Honda";
hondaCar.year = 2022;
hondaCar.displayInfo();
Enter fullscreen mode Exit fullscreen mode

Here, toyotaCar and hondaCar are objects or instances of the Car class. They hold specific data and can invoke the same behavior. This demonstrates how OOP allows for reusable, modular code.

  • An object turns the blueprint into something real - it holds actual data and can perform actions.

2. Encapsulation

Encapsulation is the process of bundling data and methods that operate on that data within a class, while restricting direct access to some of the object's components. It ensures that data is accessed only through defined interfaces, protecting it from external interference and misuse.

In simple terms, encapsulation allows us to hide the internal state of an object and expose only what is necessary for its operation.

In C++, we can achieve encapsulation using access specifiers.

class BankAccount {
private:
    double balance;

public:
    void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    void displayBalance() {
        cout << "Balance: $" << balance << endl;
    }
};

int main() {
    BankAccount account;
    account.deposit(500);
    account.displayBalance();
}
Enter fullscreen mode Exit fullscreen mode

Here, the BankAccount class represents a bank account with a private data member balance. This means that the balance cannot be accessed or modified directly from outside the class.

  • We interact with the balance using public methods like deposit and displayBalance.

3. Inheritance

Inheritance allows one class (child class) to inherit features (methods and properties) from another class (parent class).

The class that inherits is called the child class (or derived class), and the class being inherited from is called the parent class.

The child class inherits all the fields and methods of the parent class and can also add new fields and methods or override the ones inherited from the parent class.

This enables code reuse, makes the codebase DRY (Don't Repeat Yourself), and supports hierarchical classification.

Let’s say we have a parent Animal class with a method named speak().

class Animal {
public:
    void speak() {
        cout << "Animal speaks" << endl;
    }
};

class Dog : public Animal {
public:
    void bark() {
        cout << "Dog barks" << endl;
    }
};

int main() {
    Dog myDog;
    myDog.speak();  // Inherited from Animal
    myDog.bark();   // Defined in Dog
}
Enter fullscreen mode Exit fullscreen mode

Here, the Dog class inherits from the Animal class, which means it can access the speak() method or any other attribute or method defined publicly or protected in the Animal class. The Dog class also adds its own attributes and methods, such as bark().

  • This demonstrates inheritance, where a subclass inherits properties and behaviors from its parent class while adding its own unique functionality.

4. Polymorphism

Polymorphism refers to the ability of a single method to behave differently depending on the object that invokes it.

It allows you to write flexible and reusable code that can work with objects of different types, as long as they implement a shared interface.

This behavior is commonly achieved through method overriding in inheritance.

Method overriding occurs when a subclass provides its own implementation of a method that is already defined in its parent class.

For instance, let’s consider an interface Animal with a method sound().

class Animal {
public:
    virtual void sound() {
        cout << "Animal makes a sound" << endl;
    }
};

class Cat : public Animal {
public:
    void sound() override {
        cout << "Cat meows" << endl;
    }
};

int main() {
    Animal* animal = new Cat();
    animal->sound();  // Outputs "Cat meows" due to polymorphism
    delete animal;
}
Enter fullscreen mode Exit fullscreen mode

Here, the Animal class defines a virtual function sound(), which can be overridden by derived classes. Each subclass (such as Cat) of Animal implements the sound method differently (method overriding), but the interface remains consistent, allowing iteration over both classes using a single pointer.

  • This demonstrates polymorphism, where the method called depends on the actual object type, not the pointer type, enabling dynamic method binding at runtime.

5. Abstraction

Abstraction is the concept of hiding unnecessary details and showing only the relevant information. It's an extension of encapsulation, focusing more on the interface and less on the implementation.

Abstraction helps to simplify complex systems and focus on the essential features.

In C++, we can implement abstraction using abstract classes and pure virtual functions.

Let’s say we have an abstract base class called Shape. The Shape class is marked as an abstract class by inheriting from the ABC class (Abstract Base Class).

Inside the Shape class, we define an abstract method called draw().

class Shape {
public:
    virtual void draw() = 0; // Pure virtual function (abstract method)
};

class Circle : public Shape {
public:
    void draw() override {
        cout << "Drawing a Circle" << endl;
    }
};

int main() {
    Shape* shape = new Circle();
    shape->draw();
    delete shape;
}
Enter fullscreen mode Exit fullscreen mode

Here, the Circle class inherits from the Shape class.

It provides its own implementation of the draw() method specific to itself. Note that the implementation details are hidden from the outside world, and only the interface defined by the abstract class is exposed.

  • This demonstrates how Abstraction hides the implementation details (like how a circle is drawn) and only exposes the method draw() that can be called on any Shape object.

These are the fundamental principles of Object-Oriented Programming.

Implementing these concepts will help you write cleaner, reusable, and maintainable code.


💬 Did you find this useful?

If this article helped you, feel free to like and share!


Have any questions or ideas?

Leave a comment below — I'd love to hear from you!
🙌

Neon image

Build better on Postgres with AI-Assisted Development Practices

Compare top AI coding tools like Cursor and Windsurf with Neon's database integration. Generate synthetic data and manage databases with natural language.

Read more →

Top comments (0)

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay