Object-Oriented Programming: Core Principles
Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects" that contain data and code. It organizes software design around data, or objects, rather than functions and logic.
The Four Pillars of OOP
1. Encapsulation
Encapsulation is the bundling of data and methods that operate on that data within a single unit (class). It restricts direct access to some of an object's components, which is a means of preventing accidental interference and misuse.
class BankAccount {
private balance: number;
private accountNumber: string;
constructor(accountNumber: string, initialBalance: number) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
// Public method to access private data
public getBalance(): number {
return this.balance;
}
// Public method to modify private data safely
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
console.log(`Deposited $${amount}. New balance: $${this.balance}`);
} else {
console.log("Deposit amount must be positive");
}
}
public withdraw(amount: number): boolean {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
console.log(`Withdrew $${amount}. New balance: $${this.balance}`);
return true;
}
console.log("Insufficient funds or invalid amount");
return false;
}
}
// Usage
const myAccount = new BankAccount("12345", 1000);
myAccount.deposit(500);
console.log(myAccount.getBalance()); // 1500
// myAccount.balance = 10000; // ERROR: Cannot access private property2. Abstraction
Abstraction means hiding complex implementation details and showing only the necessary features of an object. It helps reduce programming complexity and effort.
// Abstract class defining the interface
abstract class Vehicle {
protected brand: string;
constructor(brand: string) {
this.brand = brand;
}
// Abstract method - must be implemented by derived classes
abstract startEngine(): void;
abstract stopEngine(): void;
// Concrete method - shared by all vehicles
public displayInfo(): void {
console.log(`This is a ${this.brand} vehicle`);
}
}
// Concrete implementation
class Car extends Vehicle {
startEngine(): void {
console.log(`${this.brand} car engine started with key turn`);
}
stopEngine(): void {
console.log(`${this.brand} car engine stopped`);
}
}
class ElectricCar extends Vehicle {
startEngine(): void {
console.log(`${this.brand} electric car powered on silently`);
}
stopEngine(): void {
console.log(`${this.brand} electric car powered off`);
}
}
// Usage - we don't need to know implementation details
const myCar: Vehicle = new Car("Toyota");
myCar.startEngine();
myCar.displayInfo();
const myTesla: Vehicle = new ElectricCar("Tesla");
myTesla.startEngine();3. Inheritance
Inheritance allows a class to inherit properties and methods from another class. It promotes code reusability and establishes a relationship between parent and child classes.
// Base class (Parent)
class Animal {
protected name: string;
protected age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
public makeSound(): void {
console.log("Some generic animal sound");
}
public eat(): void {
console.log(`${this.name} is eating`);
}
public sleep(): void {
console.log(`${this.name} is sleeping`);
}
}
// Derived class (Child)
class Dog extends Animal {
private breed: string;
constructor(name: string, age: number, breed: string) {
super(name, age); // Call parent constructor
this.breed = breed;
}
// Override parent method
public makeSound(): void {
console.log(`${this.name} barks: Woof! Woof!`);
}
// Additional method specific to Dog
public fetch(): void {
console.log(`${this.name} is fetching the ball`);
}
}
class Cat extends Animal {
constructor(name: string, age: number) {
super(name, age);
}
// Override parent method
public makeSound(): void {
console.log(`${this.name} meows: Meow!`);
}
// Additional method specific to Cat
public scratch(): void {
console.log(`${this.name} is scratching the furniture`);
}
}
// Usage
const dog = new Dog("Buddy", 3, "Golden Retriever");
dog.makeSound(); // Buddy barks: Woof! Woof!
dog.eat(); // Buddy is eating
dog.fetch(); // Buddy is fetching the ball
const cat = new Cat("Whiskers", 2);
cat.makeSound(); // Whiskers meows: Meow!
cat.scratch(); // Whiskers is scratching the furniture4. Polymorphism
Polymorphism allows objects of different classes to be treated as objects of a common base class. It enables a single interface to represent different underlying forms (data types).
// Interface defining common behavior
interface Shape {
calculateArea(): number;
calculatePerimeter(): number;
draw(): void;
}
// Different implementations
class Circle implements Shape {
constructor(private radius: number) {}
calculateArea(): number {
return Math.PI * this.radius ** 2;
}
calculatePerimeter(): number {
return 2 * Math.PI * this.radius;
}
draw(): void {
console.log(`Drawing a circle with radius ${this.radius}`);
}
}
class Rectangle implements Shape {
constructor(private width: number, private height: number) {}
calculateArea(): number {
return this.width * this.height;
}
calculatePerimeter(): number {
return 2 * (this.width + this.height);
}
draw(): void {
console.log(`Drawing a rectangle ${this.width}x${this.height}`);
}
}
class Triangle implements Shape {
constructor(
private sideA: number,
private sideB: number,
private sideC: number
) {}
calculateArea(): number {
// Using Heron's formula
const s = (this.sideA + this.sideB + this.sideC) / 2;
return Math.sqrt(s * (s - this.sideA) * (s - this.sideB) * (s - this.sideC));
}
calculatePerimeter(): number {
return this.sideA + this.sideB + this.sideC;
}
draw(): void {
console.log(`Drawing a triangle with sides ${this.sideA}, ${this.sideB}, ${this.sideC}`);
}
}
// Polymorphism in action - same interface, different implementations
function printShapeInfo(shape: Shape): void {
shape.draw();
console.log(`Area: ${shape.calculateArea().toFixed(2)}`);
console.log(`Perimeter: ${shape.calculatePerimeter().toFixed(2)}`);
console.log("---");
}
// All shapes can be treated uniformly
const shapes: Shape[] = [
new Circle(5),
new Rectangle(4, 6),
new Triangle(3, 4, 5)
];
shapes.forEach(printShapeInfo);Benefits of OOP
Modularity: Code is organized into self-contained objects
Reusability: Objects can be reused across different parts of a program or in different programs
Maintainability: Changes to one object don't affect others, making debugging easier
Scalability: New features can be added with minimal impact on existing code
Security: Encapsulation protects data from unauthorized access
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free