cover-img

Important Concepts in Object-Oriented Programming

20 January, 2023

3

3

1

Important concepts in OOP

The four pillars of object-oriented programming (OOP) are encapsulation, inheritance, polymorphism and abstraction.

Encapsulation

Encapsulation: Encapsulation is the practice of hiding the internal details of an object and only exposing a public interface. This allows data and behaviour to be bundled together and protected from external modification, which promotes data integrity and security. For example, in Java, a class can have private fields and methods only accessed by the class's public methods.

In JavaScript, encapsulation can be achieved by using closures and the this keyword.

A closure is a function that has access to variables in its parent scope, even after the parent function has finished executing. This allows data to be hidden within the closure and only exposed through a public interface.

Here's an example of encapsulation in JavaScript using closures:

function createPerson() {
let name = "John Doe";
let age = 30;

return {
getName: function() {
return name;
},
setName: function(newName) {
name = newName;
},
getAge: function() {
return age;
},
setAge: function(newAge) {
age = newAge;
}
};
}

let person = createPerson();
console.log(person.name); //undefined
console.log(person.getName()); // "John Doe"
person.setName("Jane Smith");
console.log(person.getName()); // "Jane Smith"

In this example, the createPerson function creates an object with a name and an age property hidden within the closure. The object also has getName, setName, getAge, and setAge methods, which are the only way to access and modify the name and age properties.

Another way to encapsulate in javascript is by using the this keyword, where the this keyword refers to the object that the method is being called on.

let person = {
name: "John Doe",
age: 30,
getName: function() {
return this.name;
},
setName: function(newName) {
this.name = newName;
},
getAge: function() {
return this.age;
},
setAge: function(newAge) {
this.age = newAge;
}
};
console.log(person.name); // "John Doe"
console.log(person.age); // 30
console.log(person.getName()); // "John Doe"
person.setName("Jane Smith");
console.log(person.getName()); // "Jane Smith"

In this example, the name and age properties are directly accessible. Still, by using the this keyword inside the object's methods, it's making it clear that these properties should be accessed and modified only through these methods.

It's worth noting that these examples need to provide full encapsulation like in other languages. Still, it's providing a way to restrict direct access to the properties and make it clear that these properties should be accessed and modified only through these methods.

Inheritance

Inheritance: Inheritance is the ability of one class to inherit properties and methods from a parent class. This allows for code reuse and a more organized class hierarchy. For example, a class "Bird" can inherit properties and methods from a class "Animal" and add its unique properties and methods. In JavaScript, inheritance can be achieved through prototypes and the Object.create() method.

The Object.create() method creates a new object with a specified prototype. You can use this method to create an object that inherits from another object, like this:

let parent = {
name: "John Doe",
age: 30,
getName: function() {
return this.name;
}
};

let child = Object.create(parent);
child.name = "Jane Smith";
child.age = 5;
console.log(child.getName()); // "Jane Smith"
console.log(child.age); // 5
console.log(child.__proto__ === parent); // true

In this example, the child object is created using Object.create(parent), which sets the child object's prototype to parent. The child object has its own properties name and age, but it also inherits the getName method from the parent object, and can access its name property via the prototype chain.

Another way to achieve inheritance in javascript is by using the Object.setPrototypeOf() method, which allows you to set an object's prototype to another object.

let parent = {
name: "John Doe",
age: 30,
getName: function() {
return this.name;
}
};

let child = {
age: 5
};

Object.setPrototypeOf(child, parent);
console.log(child.getName()); // "John Doe"
console.log(child.age); // 5
console.log(child.__proto__ === parent); // true

In this example, we have created an object child with its own property age, and then set its prototype to parent object using Object.setPrototypeOf(child, parent) Now the child object has access to the properties and methods of the parent object through the prototype chain.

It's worth noting that this way of inheritance can be less performant than the Object.create() method, and it's not recommended to use __proto__ property as it's not a standard way.

It's also worth noting that javascript has a class syntax (class keyword) for creating objects which is just a syntax sugar on top of the prototype-based inheritance.

Polymorphism

Polymorphism: Polymorphism is the ability of different objects to respond to the same method call differently. This allows for objects of different types to be treated as a single type and for code to be written in a more general and flexible way. For example, a method that accepts an object of class "Animal" can accept an object of class "Bird" as well because the "Bird" class is inherited from the "Animals" class.

In JavaScript, polymorphism can be achieved through the use of function overloading and function overriding.

Function overloading is the ability of a function to handle multiple types of arguments, and function overriding is the ability of a child object to override the methods of its parent object.

Function overloading can be achieved using the arguments object, an array-like object that contains all the arguments passed to a function. You can check the type of each argument and perform different actions based on the type.

function add(a, b) {
if (typeof a === 'string' && typeof b === 'string') {
return a + ' ' + b;
} else if (typeof a === 'number' && typeof b === 'number') {
return a + b;
}
}
console.log(add(1,2)); // 3
console.log(add("Hello","world")); // "Hello world"

In this example, the add function can handle numbers and strings as arguments and perform different actions based on the type.

Function overriding can be achieved by creating an object that inherits from another object and then giving it its implementation of a method.


let parent = {
name: "John Doe",
getName: function() {
return this.name;
}
};

let child = Object.create(parent);
child.getName = function() {
return "I'm " + this.name;
};
console.log(child.getName()); // "I'm John Doe"

In this example, the child object inherits the name property and the getName method from the parent object but overrides the getName method with its implementation.

It's worth noting that JavaScript is a dynamic language, and the type of an object can be changed at runtime, so it's easy to achieve polymorphism by changing the type of an object or adding/removing properties and methods.

It's also worth noting that JavaScript does not have strict type checking, so you need to be extra careful when using polymorphism by checking the type of the arguments and using the instanceof operator to check the type of an object.

Abstraction

Abstraction: Abstraction is the ability to focus on the essential features of an object and ignore the non-essential details. This allows for the creation of abstract classes and interfaces that define a group of objects' common properties and methods without specifying their exact implementation. For example, a class "Vehicle" can be an abstract class, it can have common properties and methods for all types of vehicles, but the specific implementation of those methods will be different for different types of vehicles.

In JavaScript, abstraction can be achieved through abstract classes, interfaces, and higher-order functions.

An abstract class is a class that cannot be instantiated but can be extended by other classes. An interface is similar to an abstract class, but it only contains method signatures and cannot contain any implementation.

JavaScript does not have built-in support for abstract classes and interfaces. Still, you can achieve similar functionality by combining techniques such as closures, the Object.create() method, and the this keyword.

Here's an example of an abstract class in JavaScript:

function AbstractShape() {
if (new.target === AbstractShape) {
throw new TypeError("Cannot construct Abstract instances directly");
}
this.x = 0;
this.y = 0;
}

AbstractShape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
};

function Rectangle() {
AbstractShape.call(this);
}

Rectangle.prototype = Object.create(AbstractShape.prototype);
Rectangle.prototype.constructor = Rectangle;

Rectangle.prototype.draw = function() {
console.log("Drawing a rectangle at: (" + this.x + ", " + this.y + ")");
};

let rect = new Rectangle();
rect.move(10,10);
rect.draw(); //Drawing a rectangle at: (10, 10)

In this example, the AbstractShape class is created using a closure, it has x and y properties and a move method that are common to all shapes. The Rectangle class extends the AbstractShape class and has its draw method specific to rectangles. The AbstractShape class is not supposed to be instantiated, so it throws an error if anyone tries to instantiate it directly.

Interfaces can be achieved in javascript by creating an object with method signatures and then checking that the objects that need to implement the interface have all the methods defined in the interface.

Higher-order functions are functions that take other functions as arguments or return functions as results, it can be used for abstraction, for example:

function add(a) {
return function(b) {
return a + b;
}
}
let add5 = add(5);
console.log(add5(3)); // 8
console.log(add5(4)); // 9

Here, add is a higher-order function that takes a number as an argument and returns a new function that takes another number and returns the sum of the two numbers. This way, you can create abstraction in JavaScript.

It's worth noting that these concepts work together to create complex, modular, and reusable code and help developers design and organize their code efficiently.

3

3

1

ShowwcaseHQ
Showwcase is where developers hang out and find new opportunities together as a community

More Articles