Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions oop/Abstraction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @concept Abstraction
*
* Abstraction is a fundamental Object-Oriented Programming concept that hides
* complex implementation details and exposes only the necessary features
* of an object. It allows you to focus on what an object does instead of
* how it does it.
*
* @see {@link https://en.wikipedia.org/wiki/Abstraction_(computer_science) | Wikipedia: Abstraction}
* @see {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#abstract-classes-and-members | TypeScript Docs: Abstract Classes}
*/
export abstract class MailService {
/**
* Abstract method definition.
* Subclasses must implement this method to provide specific mail delivery logic.
*/
abstract send(): void;

/**
* Shared internal logic available to all subclasses,
* abstracting away the connection process.
*/
protected connect(): void {
console.log("Connecting to mail server...");
}
}

/**
* Concrete implementation of the abstract MailService.
*/
export class GmailService extends MailService {
send(): void {
this.connect();
console.log("Sending mail via Gmail...");
}
}
33 changes: 33 additions & 0 deletions oop/Encapsulation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @concept Encapsulation
*
* Encapsulation is the bundling of data (properties) and the methods
* that operate on that data within a single unit or class. It restricts
* direct external access to the object's internal state, promoting
* data integrity and security.
*
* @see {@link https://en.wikipedia.org/wiki/Encapsulation_(computer_programming) | Wikipedia: Encapsulation}
* @see {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#member-visibility | TypeScript Docs: Member Visibility}
*/
export class SmartWatch {
/**
* Private internal state, inaccessible directly from outside the class.
*/
private _stepCount: number = 0;

/**
* Controlled public interface to safely modify the internal state.
* @param steps - The number of steps to add.
*/
public addSteps(steps: number): void {
if (steps > 0) this._stepCount += steps;
}

/**
* Public getter to safely retrieve the internal state without allowing direct modification.
* @returns The current step count.
*/
public get steps(): number {
return this._stepCount;
}
}
33 changes: 33 additions & 0 deletions oop/Inheritance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @concept Inheritance
*
* Inheritance is a mechanism where a new class (subclass) acquires the
* properties and methods of an existing class (superclass). It promotes
* code reusability and establishes a hierarchical relationship between classes.
*
* @see {@link https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming) | Wikipedia: Inheritance}
* @see {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#extends-clauses | TypeScript Docs: Extends Clauses}
*/
export class User {
constructor(public name: string) { }

/**
* Common method available to all Users and their subclasses.
*/
login(): void {
console.log(`${this.name} logged in.`);
}
}

/**
* Admin inherits the properties (name) and methods (login) from User,
* while defining its own specialized behaviors.
*/
export class Admin extends User {
/**
* Specialized method only available to Admins.
*/
deleteUser(userName: string): void {
console.log(`Admin ${this.name} is deleting user: ${userName}`);
}
}
34 changes: 34 additions & 0 deletions oop/Polymorphism.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* @concept Polymorphism
*
* Polymorphism (meaning "many forms") allows objects of different classes
* to be treated as objects of a common superclass or interface. It enables
* a single interface to represent different underlying forms (data types).
*
* @see {@link https://en.wikipedia.org/wiki/Polymorphism_(computer_science) | Wikipedia: Polymorphism}
* @see {@link https://www.typescriptlang.org/docs/handbook/2/classes.html#implements-clauses | TypeScript Docs: Implements Clauses}
*/
export interface UIElement {
/**
* Common interface method that all implementing classes must define.
*/
render(): void;
}

/**
* Specific implementation of UIElement behaving as a TextBox.
*/
export class TextBox implements UIElement {
render(): void {
console.log("Rendering a TextBox");
}
}

/**
* Specific implementation of UIElement behaving as a Checkbox.
*/
export class Checkbox implements UIElement {
render(): void {
console.log("Rendering a Checkbox");
}
}
50 changes: 50 additions & 0 deletions oop/test/OOP.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { MailService, GmailService } from '../Abstraction';
import { SmartWatch } from '../Encapsulation';
import { User, Admin } from '../Inheritance';
import { UIElement, TextBox, Checkbox } from '../Polymorphism';

describe('TypeScript OOP Educational Examples', () => {
test('Abstraction: Should correctly use MailService and GmailService', () => {
const gmail = new GmailService();
const spy = jest.spyOn(console, 'log');

gmail.send();

expect(spy).toHaveBeenCalledWith("Connecting to mail server...");
expect(spy).toHaveBeenCalledWith("Sending mail via Gmail...");
spy.mockRestore();
});

test('Encapsulation: Should protect internal step count', () => {
const watch = new SmartWatch();
watch.addSteps(100);
expect(watch.steps).toBe(100);

watch.addSteps(-50); // should not reduce steps because step must be > 0
expect(watch.steps).toBe(100);
// watch._stepCount is inaccessible here (TypeScript error)
});

test('Inheritance: Should allow Admin to inherit User and have custom behavior', () => {
const admin = new Admin("Alice");
const spy = jest.spyOn(console, 'log');

admin.login();
admin.deleteUser("Bob");

expect(spy).toHaveBeenCalledWith("Alice logged in.");
expect(spy).toHaveBeenCalledWith("Admin Alice is deleting user: Bob");
spy.mockRestore();
});

test('Polymorphism: Should work with different UIElement types', () => {
const elements: UIElement[] = [new TextBox(), new Checkbox()];
const spy = jest.spyOn(console, 'log');

elements.forEach(el => el.render());

expect(spy).toHaveBeenCalledWith("Rendering a TextBox");
expect(spy).toHaveBeenCalledWith("Rendering a Checkbox");
spy.mockRestore();
});
});