What is OOP?
Object-Oriented Programming (OOP) is a programming paradigm that organizes software around "objects." These objects represent real-world entities, combining data (properties) and behavior (methods) into a single unit. OOP enhances code reusability, scalability, and maintainability, making it a cornerstone of modern software development.
In this blog, we'll explore the core building blocks of OOP—Inheritance, Polymorphism, Abstraction, and Encapsulation—using TypeScript examples. We'll also discuss Statics as an additional OOP concept.
Building Blocks of OOP
OOP revolves around four fundamental principles:
- Inheritance: Allows a class to inherit properties and methods from another class.
- Polymorphism: Enables objects of different types to be treated as instances of a common type.
- Abstraction: Hides complex details and exposes only the essentials.
- Encapsulation: Restricts access to an object’s internal data, ensuring controlled interaction.
Let’s dive into each with practical TypeScript examples.
Inheritance
Inheritance enables a class (child) to inherit properties and methods from another class (parent), promoting code reuse and relationships between classes.
Example: Student and Teacher
The Teacher
class inherits from Student
, gaining its properties and methods while adding its own.
class Student {
constructor(public name: string, public age: number, public address: string) {}
getSleep(hours: number) {
console.log(`${this.name} will sleep for ${hours}`);
}
}
class Teacher extends Student {
constructor(name: string, age: number, address: string, public designation: string) {
super(name, age, address);
}
takeClass(hours: number) {
console.log(`${this.name} takes ${hours} of class each day`);
}
}
const student1 = new Student('asif', 30, 'Dhaka');
student1.getSleep(5); // asif will sleep for 5
const teacher1 = new Teacher('Tarek', 34, 'Chittagong', 'Math Teacher');
teacher1.getSleep(7); // Tarek will sleep for 7
teacher1.takeClass(3); // Tarek takes 3 of class each day
The super
keyword initializes the parent class, ensuring proper setup.
Polymorphism
Polymorphism allows objects of different classes to be treated as instances of a shared superclass, enabling flexible and reusable code.
Example: Sleeping Hours
Here, Student
and Developer
override the getSleep
method from Person
, yet all work with a single function.
class Person {
getSleep() {
console.log("A person sleep for 8 hours!");
}
}
class Student extends Person {
getSleep() {
console.log("A person sleep for 7 hours!");
}
}
class Developer extends Person {
getSleep() {
console.log("A person sleep for 6 hours!");
}
}
function getSleepingHours(instance: Person) {
return instance.getSleep();
}
const abul = new Person();
const kabul = new Student();
const habul = new Developer();
getSleepingHours(abul); // A person sleep for 8 hours!
getSleepingHours(kabul); // A person sleep for 7 hours!
getSleepingHours(habul); // A person sleep for 6 hours!
This demonstrates how polymorphism unifies behavior across subclasses.
Abstraction
Abstraction simplifies systems by hiding implementation details and exposing only necessary interfaces. TypeScript supports this with interfaces and abstract classes.
Example: Vehicles
An interface Vehicle1
defines a contract, while an abstract class Vehicle2
provides a base with abstract methods.
// Interface: purely defines a contract
interface Vehicle1 {
startEngine(): void;
stopEngine(): void;
}
// Abstract class: defines abstract methods
abstract class Vehicle2 {
abstract startEngine(): void;
abstract move(): void;
abstract stopEngine(): void;
}
// Class implementing the interface
class Car1 implements Vehicle1 {
startEngine(): void {
console.log("starting engine...");
}
stopEngine(): void {
console.log("stopping engine");
}
}
// Class extending the abstract class
class Car2 extends Vehicle2 {
startEngine(): void {
console.log("starting engine");
}
move(): void {
console.log("Moving car");
}
stopEngine(): void {
console.log("stopping engine..");
}
}
const car1 = new Car1();
car1.startEngine(); // starting engine...
car1.stopEngine(); // stopping engine
const car2 = new Car2();
car2.startEngine(); // starting engine
car2.move(); // Moving car
car2.stopEngine(); // stopping engine..
Car1
implements Vehicle1
, while Car2
extends Vehicle2
, fulfilling their contracts.
Encapsulation
Encapsulation protects an object’s internal state using access modifiers (public
, protected
, private
) and getters/setters.
Example: Bank Account
The BankAcc
class controls access to its data:
class BankAcc {
constructor(
readonly id: string,
protected name: string,
private _balance: number
) {}
addDeposit(amount: number) {
this._balance += amount;
}
get balance() {
return this._balance;
}
set deposit(amount: number) {
this._balance += amount;
}
}
class StudentAcc extends BankAcc {
getName() {
return this.name; // Accessible because it’s protected
}
}
const acc1 = new BankAcc('111', 'rob', 42000);
acc1.addDeposit(33);
console.log(acc1.balance); // 42033
const s1 = new StudentAcc('222', 'sifat', 43);
console.log(s1.getName()); // sifat
s1.deposit = 1;
console.log(s1.balance); // 44
-
id
isreadonly
, immutable post-creation. -
name
isprotected
, accessible to subclasses. -
_balance
isprivate
, managed via methods.
Statics
Static members belong to the class itself, not instances, and are shared across all objects.
Example: Counter
class Counter {
static count: number = 0;
increment() {
return (Counter.count += 1);
}
decrement() {
return (Counter.count -= 1);
}
}
const instance1 = new Counter();
console.log(instance1.increment()); // 1
const instance2 = new Counter();
console.log(instance2.increment()); // 2
console.log(instance1.decrement()); // 1
The count
property is static, maintaining a single value across instances.
Conclusion
OOP in TypeScript provides powerful tools to structure code effectively. Through Inheritance, Polymorphism, Abstraction, Encapsulation, and Statics, you can build robust, reusable, and maintainable applications. Experiment with these examples to master OOP principles in your projects!
Top comments (0)