OOP Principles, JavaScript Perspective
OOP design principles explained with JS/TS examples.
Table of contents
The object-oriented programming paradigm has 4 core principles,
- Encapsulation
- Abstraction
- Inheritance
- Polymorphism
Encapsulation
Used for data hiding.
In the following example, the Movie
class has two private properties,
- _title
- _logs
But these properties are hidden and only be get or set with their getter and setter method.
class Movie {
private _title: string;
private _logs: Array<string>;
constructor(title = '') {
this._title = title;
this._logs = [];
}
public get title() {
this._logs.push(`Getting title: ${this._title}`);
return this._title;
}
public set title(title: string) {
this._logs.push(`Setting title to: ${title}`);
this._title = title;
}
public get logs() {
return this._logs;
}
}
const movie = new Movie();
movie.title = 'Aguner Porosmoni';
console.log(movie.title);
console.log(movie.logs); // ["Setting title to: Aguner Porosmoni", "Getting title: Aguner Porosmoni"]
// If we directly try to access the properties like `_title` or `_logs`, like below, throws an error
// Property '_title' is private and only accessible within the class 'Movie'.
// movie._title = 'Aguner Porosmoni';
Here title
is not accessed directly, instead, we need to use getter and setter that are available outside to interact with the title
property.
Abstraction
A technique to simplify programming structure. Abstraction hides not necessary details and minimizes complexity.
For example, pressing the gas pedal of a car increases the speed. For drivers not necessary to reveal how gas is consumed by the engine and internal mechanism.
class Circle {
constructor(radius = 0) {
this.radius = radius;
this.pi = 3.14;
}
getArea() {
return this.pi * Math.pow(this.radius, 2);
}
}
Here we pass the radius and call getArea
to get the area of a circle. Abstruction hides how the area calculation is being done.
Inheritance
Inheritance provides a way to create a new class from an existing class. This new class can access all the non-private properties of the existing class.
For example, we have a class called Shape
. We can create a new class called Square
from the Shape
class.
Inheritance can be,
- Single inheritance (FuelCar inherits Vehicle class)
- Multiple inheritances (HybridCar inherits both ElectricCar and FuelCar classes)
- Multi-level inheritance (GasolineCar inherits FuelCar and FuelCar inherits Vehicle class)
- Hierarchical inheritance (Both FuelCar and ElectricCar inherits )
- Hybrid inheritance
Polymorphism
Polymorphism allows calling methods with different signatures.
It can be
- Method Overriding
- Method Overloading
In the following example, we override a method called multiply
. Both parent class and child class has the same method but different parameter. When we pass 3 parameters, it is handled by the childClass
object. For two parameters, the object of ParentClass
is invoked.
class ParentClass {
multiply(a, b) {
console.log(a * b);
}
}
class ChildClass extends ParentClass {
multiply(a, b, c) {
// super.multiply(a, b);
console.log(a * b * c);
}
}
const parent = new ParentClass();
const child = new ChildClass();
parent.multiply(2, 3); // 6
child.multiply(2, 3, 4); // 24
For method overloading, we can consider the following example. We have getArea
method and can take a single or double parameter. If we get both height
and width
, we return height * width
. Otherwise, for only height, return height * height
.
class Shape {
getArea(height, width = -1) {
if (width === -1) {
return height * height;
}
return height * width;
}
}
const shape = new Shape();
shape.getArea(5); // 25
shape.getArea(5, 6); // 30
Abstraction vs Encapsulation
- Abstraction is design level, Encapsulation is application level
- Abstraction hides not necessary data, and Encapsulation restricts access to prevent misuse
- Abstraction uses an interface and abstract class to hide data, and Encapsulation uses a getter and setter to prevent direct access