The Four Pillars of Object Oriented Programming

The Four Pillars of Object Oriented Programming

Object-oriented programming (OOP) is a programming paradigm that focuses on modeling the world as objects and their interactions. It revolves around four key principles: encapsulation, abstraction, inheritance, and polymorphism. OOP aims to create reusable and modular code that is easy to understand, maintain, and extend.

Object-Oriented Programming (OOP) 

Object-Oriented Programming (OOP) is a programming paradigm which many companies like Scrrum Labs use which is  based on the concept of "objects"  . In JavaScript, OOP is implemented through the use of "prototypal inheritance", which allows objects to inherit properties and methods from other objects.

The four pillars of Object-Oriented Programming (OOP) are inheritance, encapsulation, abstraction, and polymorphism

Inheritance

Inheritance allows objects to inherit properties and methods from other objects  which is very useful in our  application. In JavaScript, inheritance is achieved through the use of prototypes. Every object in JavaScript has a prototype, which is an object that the current object inherits properties and methods from. The prototype chain is a series of objects linked together through their prototype properties. When a property or method is accessed on an object,

An Example of Inheritance in Javascript

// Create a parent object
const animal = {
  name: "",
  sound: "",
  setName: function(name) {
    this.name = name;
  },
  makeSound: function() {
    console.log(this.sound);
  }
};

// Create a child object that inherits from the parent object
const cat = Object.create(animal);
cat.sound = "Meow";
cat.setName("Fluffy");

// Call methods on the child object
cat.makeSound(); // Output: "Meow"
console.log(cat.name); // Output: "Fluffy"

In this example, the cat object inherits the setName() and makeSound() methods from the animal object. By using inheritance, we can create objects that share common properties and methods without having to define them on each object separately. This can make our code more efficient and easier to maintain.

Encapsulation:

Encapsulation refers to the practice of hiding the internal details of an object from the outside world. In JavaScript, encapsulation is achieved through the use of closures. Closures allow variables and functions to be defined within a function and accessed only by that function and any nested functions. This allows the internal details of an object to be hidden from the outside world so our project's important stuff stays hidden .

// Create a class with public and private properties
class BankAccount {
  constructor(owner, balance) {
    this.owner = owner; // Public property
    let _balance = balance; // Private property (using a closure)
    
    
    // Public method that can access private properties and methods
    this.getBalance = function() {
      return formatCurrency(_balance);
    }
    
   
    
   
  }
}

// Create an instance of the BankAccount class
const account = new BankAccount("John Doe", 1000);

// Try to access the private properties directly (which should fail)
console.log(account._balance); // Output: undefined

// Access the public properties and methods
console.log(account.owner); // Output: "John Doe"
console.log(account.getBalance()); // Output: "$1000.00"


By encapsulating the private properties and methods, we can ensure that the internal state of the BankAccount object is protected from outside interference. This helps us write more robust and reliable code that is less prone to errors and security vulnerabilities.

Abstraction:

Abstraction is the process of simplifying complex systems by breaking them down into smaller, more manageable parts. In JavaScript, abstraction can be achieved through the use of classes and interfaces. Classes are templates for creating objects that have properties and methods. Interfaces are contracts that specify the properties and methods that an object must have in order to be considered a member of a particular class.

// Create a parent class
class Vehicle {
  constructor() {
    if (new.target === Vehicle) {
      throw new TypeError("Cannot create instances of abstract class Vehicle");
    }
  }
  
  // Define an abstract method that must be implemented by child classes
  start() {
    throw new Error("Method 'start' must be implemented by child classes");
  }
}

// Create a child class that extends the parent class
class Car extends Vehicle {
  start() {
    console.log("Starting the car...");
  }
}

// Try to create an instance of the parent class (which is abstract)
const vehicle = new Vehicle(); // Throws an error

// Create an instance of the child class and call its start method
const car = new Car();
car.start(); // Output: "Starting the car..."

In this example, we have a parent class Vehicle that defines an abstract method start. We also have a child class Car that extends the Vehicle class and provides its own implementation of the start abstract method.

Polymorphism:

Polymorphism is the ability of objects to take on different forms or behaviors. In JavaScript, polymorphism is achieved through the use of "duck typing". Duck typing is a programming concept that allows objects to be treated as if they are of a certain type if they have the same properties and methods as objects of that type. This allows for greater flexibility in programming, as objects can be reused in multiple contexts without having to create separate objects for each context.

// Create a parent class
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  makeSound() {
    console.log("This animal makes a sound.");
  }
}

// Create a child class that extends the parent class
class Dog extends Animal {
  constructor(name) {
    super(name);
  }
  
  makeSound() {
    console.log("Woof!");
  }
}

// Create another child class that extends the parent class
class Cat extends Animal {
  constructor(name) {
    super(name);
  }
  
  makeSound() {
    console.log("Meow!");
  }
}

// Create instances of the child classes
const dog = new Dog("Fido");
const cat = new Cat("Fluffy");

// Call the makeSound method on each instance
dog.makeSound(); // Output: "Woof!"
cat.makeSound(); // Output: "Meow!"

When we create instances of the Dog and Cat classes and call the makeSound method, each instance uses its own implementation . This is an example of polymorphism, where different objects can take on different forms or behaviors while still adhering to the same interface (i.e., the makeSound method in this case).

Shape

Drop your comment