The 4 Essential Pillars of Object-Oriented Programming Explained

AbhinavAbhinav
5 min read

Hello everyone,

Today we are going to learn about a core concept of software development: Object-Oriented Programming (OOP). In this article, we will go through the key principles of classical or traditional OOP.

Object-Oriented Programming (OOP) is often associated with object-oriented languages, but that doesn't mean we can't use its principles in other languages. Modern OOP concepts have introduced many valuable changes, allowing us to apply such concepts in functional programming languages too. Therefore, the code snippets in this article will be in JavaScript.

First of All, What is Object Oriented Programming?

OOP is a programming paradigm used in Object-Oriented (OO) languages like Java and C++. In OOP, we create and model objects that contain functions and data. These data and functions can be accessed by other objects, creating a system of interconnected objects. This system helps in building complex programs that require multiple data and functions to work together.

Note: Here, Objects are the instance of class which means in language we made classes with methods and properties but it actually store as a object in the memory alongwith its properties and functions.

Key Principles of Classical OOP

In Classical OOPs, four principles are used to define objects and its properties, they also known as Pillars of OOP:

  1. Abstraction

    Hiding the internal properties of objects (classes) to prevent access by other objects is known as Abstraction. In simple terms, you don't need to know how the engine works; you just need to drive the car.This helps reduce code complexity and allows developers to use objects without worrying about their internal details and logics.

     class Car {
     #checkEngine //declaring of Private Field
     constructor(brand, model){
     this.brand = brand;
     this.model = model;
      }
     #checkEngine() {
     console.log('Checking the engine.....')
     // internal logics
      }
     start() {
     this.#checkEngine();
     console.log(`${this.brand} ${this.model} is starting...`);
      }
     }
     const myCar = new Car('Toyota','Camry');
     myCar.start();
     myCar.#checkEngine();
     // Output :
     /*
     Checking the engine.....
     Toyota Camry is starting...
     Error
     */
    

    In the code above, the checkEngine() function is an internal logic and a private field, meaning it cannot be accessed by myCar. However, the start() function uses it internally. Therefore, myCar can only access the start() function, which is intended for external use.

  2. Encapsulation

    It is a method that restrict the access of internal logics or function of class to not get changed by external objects and can only be change in it's own class or object. It helps in making codebase more safer and secure. Also it reduces the chances of interference and misuse of code.

     class Car {
     #isEnginePresent //declaring of Private Field
     constructor(brand, model){
     this.brand = brand;
     this.model = model;
     this.#isEnginePresent = false; // 
     }
     #checkEngine() {
     console.log('Checking the engine.....');
     // internal logics
      }
     setEngineAvailable() {
     this.#isEnginePresent = true;
     }
     getEngineAvailable() {
     console.log(this.#isEnginePresent);
     }
     start() {
     this.#checkEngine();
     console.log(`${this.brand} ${this.model} is starting...`);
      }
     }
     const myCar = new Car('Toyota','Camry');
     myCar.start();
     const CurrentBrand = myCar.brand;
     console.log(CurrentBrand);
     myCar.setEngineAvailable();
     myCar.getEngineAvailable();
     // Output :
     /*
     Checking the engine.....
     Toyota Camry is starting...
     Toyota
     True
     */
    

    In above code, you can see that i added a private field of name isEnginePresent with default value false but when we run the function getEngineAvailable() in which we added code to print isEnginePresent value so it prints a true value. As per definition, it must not be possible to change the value of a private field. Yes, you are right and that's why we use getter(getEngineAvailable()) and setter(setEngineAvailable()) here. Through this functions we set a new value and get that value but we cannot access that field from myCar as like we access Brand name of myCar.

  3. Inheritance

    As the name suggest, it is a method in which children class or subclass inherit properties and method from their parent class alongwith their own different properties and methods.

     class Person {
     constructor(name) {
         this.name = name;
      }
     }
     class Boy extends Person {
     constructor(name, age) {
          super(name)
          this.age = age
      }
     }
     const BoyA = new Boy("Rahul", 14);
     console.log(BoyA.name, BoyA.age);
    

    In the code above, you can see that we first create a Person class and then a Boy class. In the Boy class, we inherit the name property from the Person class and add an age property. Therefore, Boy is a subclass that inherits properties from its parent class, Person.

  4. Polymorphism

    A method in which same naming functions are used with different implementation in different classes - is called polymorphism. It generally means the subclass overrides the values of it's parent class from it's own values.

     class Person {
     constructor(name) {
         this.name = name;
      }
     introduction() {
       console.log(`Hello I am ${this.name}`)
      }
     }
     class Boy extends Person {
     constructor(name, age) {
          super(name)
          this.age = age
      }
     }
     const PersonA = new Person("Rajesh");
     const BoyA = new Boy("Rahul", 14);
     PersonA.introduction();
     BoyA.introduction();
     // Output: Hello I am Rajesh
     //         Hello I am Rahul
    

    In the code above, we see that the introduction function is added to the Person class with its value. The Boy class also gets that function and uses it with its own value, "Rahul." This happens because Boy inherits the function from its parent class and overrides it with its own value.

Conclusion

In this article, we explored the core principles of Object-Oriented Programming (OOP) using JavaScript examples. We covered the four main pillars of traditional OOP: Abstraction, Encapsulation, Inheritance, and Polymorphism. Each principle was explained with code snippets to demonstrate how these concepts are implemented and used in real-world programming scenarios.

Understanding these principles is crucial for building robust, maintainable, and scalable software. By mastering these concepts, developers can write cleaner code, reduce complexity, and enhance collaboration within development teams.

If you want to delve deeper into these concepts, you can check out the following resources:

Thank you for taking the time to read this post! I would love to get your feedback on this article. Please feel free to share your thoughts, questions, or experiences in the comment section below. Your insights not only help me to improve but also enrich the discussion for everyone.

Thank you once again, and see you in the next article.

1
Subscribe to my newsletter

Read articles from Abhinav directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Abhinav
Abhinav

👨‍💻Hi there! I am Abhinav | Web Developer in the Making | Tech Blogger @ Hashnode | AI/ML Enthusiast