From 5676fec886be7430f8153fae8d65d52c66aa03a9 Mon Sep 17 00:00:00 2001 From: Samrat Saurav Jaiswal <39924445+ictrl@users.noreply.github.com> Date: Sun, 1 Feb 2026 01:16:26 +0530 Subject: [PATCH] feat: add TypeScript for Lecture 05 --- Lecture 05/Typescript/LSP/LSPFollowed.ts | 101 ++++++++++++++++++ .../Typescript/LSP/LSPFollowedWrongly.ts | 98 +++++++++++++++++ Lecture 05/Typescript/LSP/LSPViolated.ts | 95 ++++++++++++++++ Lecture 05/Typescript/OCP/OCPFollowed.ts | 100 +++++++++++++++++ Lecture 05/Typescript/OCP/OCPViolated.ts | 92 ++++++++++++++++ Lecture 05/Typescript/SRP/SRPFollowed.ts | 85 +++++++++++++++ Lecture 05/Typescript/SRP/SRPViolated.ts | 66 ++++++++++++ 7 files changed, 637 insertions(+) create mode 100644 Lecture 05/Typescript/LSP/LSPFollowed.ts create mode 100644 Lecture 05/Typescript/LSP/LSPFollowedWrongly.ts create mode 100644 Lecture 05/Typescript/LSP/LSPViolated.ts create mode 100644 Lecture 05/Typescript/OCP/OCPFollowed.ts create mode 100644 Lecture 05/Typescript/OCP/OCPViolated.ts create mode 100644 Lecture 05/Typescript/SRP/SRPFollowed.ts create mode 100644 Lecture 05/Typescript/SRP/SRPViolated.ts diff --git a/Lecture 05/Typescript/LSP/LSPFollowed.ts b/Lecture 05/Typescript/LSP/LSPFollowed.ts new file mode 100644 index 0000000..e84c597 --- /dev/null +++ b/Lecture 05/Typescript/LSP/LSPFollowed.ts @@ -0,0 +1,101 @@ +// 1. DepositOnlyAccount interface: only allows deposits +interface DepositOnlyAccount { + deposit(amount: number): void +} + +// 2. WithdrawableAccount interface: allows deposits and withdrawals +interface WithdrawableAccount extends DepositOnlyAccount { + withdraw(amount: number): void +} + +class SavingAccount implements WithdrawableAccount { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Savings Account. New Balance: ${this.balance}`) + } + + withdraw(amount: number): void { + if (this.balance >= amount) { + this.balance -= amount + console.log(`Withdrawn: ${amount} from Savings Account. New Balance: ${this.balance}`) + } else { + console.log("Insufficient funds in Savings Account!") + } + } +} + +class CurrentAccount implements WithdrawableAccount { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Current Account. New Balance: ${this.balance}`) + } + + withdraw(amount: number): void { + if (this.balance >= amount) { + this.balance -= amount + console.log(`Withdrawn: ${amount} from Current Account. New Balance: ${this.balance}`) + } else { + console.log("Insufficient funds in Current Account!") + } + } +} + +class FixedTermAccount implements DepositOnlyAccount { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Fixed Term Account. New Balance: ${this.balance}`) + } +} + +class BankClient { + private withdrawableAccounts: WithdrawableAccount[] + private depositOnlyAccounts: DepositOnlyAccount[] + + constructor( + withdrawableAccounts: WithdrawableAccount[], + depositOnlyAccounts: DepositOnlyAccount[] + ) { + this.withdrawableAccounts = withdrawableAccounts + this.depositOnlyAccounts = depositOnlyAccounts + } + + processTransactions(): void { + for (const acc of this.withdrawableAccounts) { + acc.deposit(1000) + acc.withdraw(500) + } + + for (const acc of this.depositOnlyAccounts) { + acc.deposit(5000) + } + } +} + +function main(): void { + const withdrawableAccounts: WithdrawableAccount[] = [ + new SavingAccount(), + new CurrentAccount() + ] + + const depositOnlyAccounts: DepositOnlyAccount[] = [ + new FixedTermAccount() + ] + + const client = new BankClient(withdrawableAccounts, depositOnlyAccounts) + client.processTransactions() +} + +main() + +/** + * If a .ts file has no imports or exports, TypeScript treats it as a global script. + * Adding `export {}` marks the file as a module and prevents + * "Duplicate identifier 'SavingAccount'" errors. + */ +export {} \ No newline at end of file diff --git a/Lecture 05/Typescript/LSP/LSPFollowedWrongly.ts b/Lecture 05/Typescript/LSP/LSPFollowedWrongly.ts new file mode 100644 index 0000000..f8d1494 --- /dev/null +++ b/Lecture 05/Typescript/LSP/LSPFollowedWrongly.ts @@ -0,0 +1,98 @@ +// Account interface +interface Account { + deposit(amount: number): void + withdraw(amount: number): void +} + +class SavingAccount implements Account { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Savings Account. New Balance: ${this.balance}`) + } + + withdraw(amount: number): void { + if (this.balance >= amount) { + this.balance -= amount + console.log(`Withdrawn: ${amount} from Savings Account. New Balance: ${this.balance}`) + } else { + console.log("Insufficient funds in Savings Account!") + } + } +} + +class CurrentAccount implements Account { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Current Account. New Balance: ${this.balance}`) + } + + withdraw(amount: number): void { + if (this.balance >= amount) { + this.balance -= amount + console.log(`Withdrawn: ${amount} from Current Account. New Balance: ${this.balance}`) + } else { + console.log("Insufficient funds in Current Account!") + } + } +} + +class FixedTermAccount implements Account { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Fixed Term Account. New Balance: ${this.balance}`) + } + + withdraw(amount: number): void { + throw new Error("Withdrawal not allowed in Fixed Term Account!") + } +} + +class BankClient { + private accounts: Account[] + + constructor(accounts: Account[]) { + this.accounts = accounts + } + + processTransactions(): void { + for (const acc of this.accounts) { + acc.deposit(1000) + + // Explicit type checking instead of true substitutability + if (acc instanceof FixedTermAccount) { + console.log("Skipping withdrawal for Fixed Term Account.") + } else { + try { + acc.withdraw(500) + } catch (e: any) { + console.log(`Exception: ${e.message}`) + } + } + } + } +} + +function main(): void { + const accounts: Account[] = [] + accounts.push(new SavingAccount()) + accounts.push(new CurrentAccount()) + accounts.push(new FixedTermAccount()) + + const client = new BankClient(accounts) + client.processTransactions() +} + +main() + +/** + * If a .ts file has no imports or exports, TypeScript treats it as a global script. + * Adding `export {}` marks the file as a module and prevents + * "Duplicate identifier 'SavingAccount'" errors. + */ +export {} \ No newline at end of file diff --git a/Lecture 05/Typescript/LSP/LSPViolated.ts b/Lecture 05/Typescript/LSP/LSPViolated.ts new file mode 100644 index 0000000..1c74927 --- /dev/null +++ b/Lecture 05/Typescript/LSP/LSPViolated.ts @@ -0,0 +1,95 @@ +// Account interface +interface Account { + deposit(amount: number): void + withdraw(amount: number): void +} + +class SavingAccount implements Account { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Savings Account. New Balance: ${this.balance}`) + } + + withdraw(amount: number): void { + if (this.balance >= amount) { + this.balance -= amount + console.log(`Withdrawn: ${amount} from Savings Account. New Balance: ${this.balance}`) + } else { + console.log("Insufficient funds in Savings Account!") + } + } +} + +class CurrentAccount implements Account { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Current Account. New Balance: ${this.balance}`) + } + + withdraw(amount: number): void { + if (this.balance >= amount) { + this.balance -= amount + console.log(`Withdrawn: ${amount} from Current Account. New Balance: ${this.balance}`) + } else { + console.log("Insufficient funds in Current Account!") + } + } +} + +class FixedTermAccount implements Account { + private balance: number = 0 + + deposit(amount: number): void { + this.balance += amount + console.log(`Deposited: ${amount} in Fixed Term Account. New Balance: ${this.balance}`) + } + + withdraw(amount: number): void { + throw new Error("Withdrawal not allowed in Fixed Term Account!") + } +} + +class BankClient { + private accounts: Account[] + + constructor(accounts: Account[]) { + this.accounts = accounts + } + + processTransactions(): void { + for (const acc of this.accounts) { + acc.deposit(1000) + + // LSP violation: assuming all accounts support withdrawal + try { + acc.withdraw(500) + } catch (e: any) { + console.log(`Exception: ${e.message}`) + } + } + } +} + +// Equivalent of Java main +function main(): void { + const accounts: Account[] = [] + accounts.push(new SavingAccount()) + accounts.push(new CurrentAccount()) + accounts.push(new FixedTermAccount()) + + const client = new BankClient(accounts) + client.processTransactions() +} + +main() + +/** + * If a .ts file has no imports or exports, TypeScript treats it as a global script. + * Adding `export {}` marks the file as a module and prevents + * "Duplicate identifier 'SavingAccount'" errors. + */ +export {} \ No newline at end of file diff --git a/Lecture 05/Typescript/OCP/OCPFollowed.ts b/Lecture 05/Typescript/OCP/OCPFollowed.ts new file mode 100644 index 0000000..1ab94c4 --- /dev/null +++ b/Lecture 05/Typescript/OCP/OCPFollowed.ts @@ -0,0 +1,100 @@ +// Product class representing any item in eCommerce. +class Product { + public name: string + public price: number + + constructor(name: string, price: number) { + this.name = name + this.price = price + } +} + +// 1. ShoppingCart: Only responsible for cart related business logic. +class ShoppingCart { + private products: Product[] = [] + + public addProduct(p: Product): void { + this.products.push(p) + } + + public getProducts(): Product[] { + return this.products + } + + // Calculates total price in cart. + public calculateTotal(): number { + let total = 0 + for (const p of this.products) { + total += p.price + } + return total + } +} + +// 2. ShoppingCartPrinter: Only responsible for printing invoices +class ShoppingCartPrinter { + private cart: ShoppingCart + + constructor(cart: ShoppingCart) { + this.cart = cart + } + + public printInvoice(): void { + console.log("Shopping Cart Invoice:") + for (const p of this.cart.getProducts()) { + console.log(`${p.name} - Rs ${p.price}`) + } + console.log(`Total: Rs ${this.cart.calculateTotal()}`) + } +} + +// Persistence abstraction +interface Persistence { + save(cart: ShoppingCart): void +} + +class SQLPersistence implements Persistence { + save(cart: ShoppingCart): void { + console.log("Saving shopping cart to SQL DB...") + } +} + +class MongoPersistence implements Persistence { + save(cart: ShoppingCart): void { + console.log("Saving shopping cart to MongoDB...") + } +} + +class FilePersistence implements Persistence { + save(cart: ShoppingCart): void { + console.log("Saving shopping cart to a file...") + } +} + +// Equivalent of Java main +function main(): void { + const cart = new ShoppingCart() + cart.addProduct(new Product("Laptop", 50000)) + cart.addProduct(new Product("Mouse", 2000)) + + const printer = new ShoppingCartPrinter(cart) + printer.printInvoice() + + const db: Persistence = new SQLPersistence() + const mongo: Persistence = new MongoPersistence() + const file: Persistence = new FilePersistence() + + db.save(cart) + mongo.save(cart) + file.save(cart) +} + +main() + + +/** + * If a .ts file has no imports or exports, TypeScript treats it as a global script. + * Adding `export {}` marks the file as a module and prevents + * "Duplicate identifier 'ShoppingCart'" errors. + */ +export {} \ No newline at end of file diff --git a/Lecture 05/Typescript/OCP/OCPViolated.ts b/Lecture 05/Typescript/OCP/OCPViolated.ts new file mode 100644 index 0000000..a3b5ee4 --- /dev/null +++ b/Lecture 05/Typescript/OCP/OCPViolated.ts @@ -0,0 +1,92 @@ +// Product class representing any item in eCommerce. +class Product { + name: string + price: number + + constructor(name: string, price: number) { + this.name = name + this.price = price + } +} + +// 1. ShoppingCart: Only responsible for cart related business logic. +class ShoppingCart { + private products: Product[] = [] + + addProduct(p: Product): void { + this.products.push(p) + } + + getProducts(): Product[] { + return this.products + } + + calculateTotal(): number { + let total = 0 + for (const p of this.products) { + total += p.price + } + return total + } +} + +// 2. ShoppingCartPrinter: Only responsible for printing invoices +class ShoppingCartPrinter { + private cart: ShoppingCart + + constructor(cart: ShoppingCart) { + this.cart = cart + } + + printInvoice(): void { + console.log("Shopping Cart Invoice:") + for (const p of this.cart.getProducts()) { + console.log(`${p.name} - Rs ${p.price}`) + } + console.log(`Total: Rs ${this.cart.calculateTotal()}`) + } +} + +// 3. ShoppingCartStorage: OCP VIOLATED +class ShoppingCartStorage { + private cart: ShoppingCart + + constructor(cart: ShoppingCart) { + this.cart = cart + } + + saveToSQLDatabase(): void { + console.log("Saving shopping cart to SQL DB...") + } + + saveToMongoDatabase(): void { + console.log("Saving shopping cart to Mongo DB...") + } + + saveToFile(): void { + console.log("Saving shopping cart to File...") + } +} + +// Equivalent of Java main +function main(): void { + const cart = new ShoppingCart() + + cart.addProduct(new Product("Laptop", 50000)) + cart.addProduct(new Product("Mouse", 2000)) + + const printer = new ShoppingCartPrinter(cart) + printer.printInvoice() + + const db = new ShoppingCartStorage(cart) + db.saveToSQLDatabase() +} + +main() + +/** + * If a .ts file has no imports or exports, TypeScript treats it as a global script. + * Adding `export {}` marks the file as a module and prevents + * "Duplicate identifier 'ShoppingCart'" errors. + */ +export {} diff --git a/Lecture 05/Typescript/SRP/SRPFollowed.ts b/Lecture 05/Typescript/SRP/SRPFollowed.ts new file mode 100644 index 0000000..fc64f2c --- /dev/null +++ b/Lecture 05/Typescript/SRP/SRPFollowed.ts @@ -0,0 +1,85 @@ +// Product class representing any item in eCommerce. +class Product { + public name: string + public price: number + + constructor(name: string, price: number) { + this.name = name + this.price = price + } +} + +// 1. ShoppingCart: Only responsible for cart related business logic. +class ShoppingCart { + private products: Product[] = [] + + public addProduct(p: Product): void { + this.products.push(p) + } + + public getProducts(): Product[] { + return this.products + } + + // Calculates total price in cart. + public calculateTotal(): number { + let total = 0 + for (const p of this.products) { + total += p.price + } + return total + } +} + +// 2. ShoppingCartPrinter: Only responsible for printing invoices +class ShoppingCartPrinter { + private cart: ShoppingCart + + constructor(cart: ShoppingCart) { + this.cart = cart + } + + public printInvoice(): void { + console.log("Shopping Cart Invoice:") + for (const p of this.cart.getProducts()) { + console.log(`${p.name} - Rs ${p.price}`) + } + console.log(`Total: Rs ${this.cart.calculateTotal()}`) + } +} + +// 3. ShoppingCartStorage: Only responsible for saving cart to DB +class ShoppingCartStorage { + private cart: ShoppingCart + + constructor(cart: ShoppingCart) { + this.cart = cart + } + + public saveToDatabase(): void { + console.log("Saving shopping cart to database...") + } +} + +// Equivalent of Java main +function main(): void { + const cart = new ShoppingCart() + + cart.addProduct(new Product("Laptop", 50000)) + cart.addProduct(new Product("Mouse", 2000)) + + const printer = new ShoppingCartPrinter(cart) + printer.printInvoice() + + const db = new ShoppingCartStorage(cart) + db.saveToDatabase() +} + +main() + +/** + * If a .ts file has no imports or exports, TypeScript treats it as a global script. + * Adding `export {}` marks the file as a module and prevents + * "Duplicate identifier 'ShoppingCart'" errors. + */ +export {} \ No newline at end of file diff --git a/Lecture 05/Typescript/SRP/SRPViolated.ts b/Lecture 05/Typescript/SRP/SRPViolated.ts new file mode 100644 index 0000000..88d594d --- /dev/null +++ b/Lecture 05/Typescript/SRP/SRPViolated.ts @@ -0,0 +1,66 @@ +// Product class representing any item of any ECommerce. +class Product { + public name: string + public price: number + + constructor(name: string, price: number) { + this.name = name + this.price = price + } +} + +// Violating SRP: ShoppingCart is handling multiple responsibilities +class ShoppingCart { + private products: Product[] = [] + + public addProduct(p: Product): void { + this.products.push(p) + } + + public getProducts(): Product[] { + return this.products + } + + // 1. Calculates total price in cart. + public calculateTotal(): number { + let total = 0 + for (const p of this.products) { + total += p.price + } + return total + } + + // 2. Violating SRP: Prints invoice + public printInvoice(): void { + console.log("Shopping Cart Invoice:") + for (const p of this.products) { + console.log(`${p.name} - Rs ${p.price}`) + } + console.log(`Total: Rs ${this.calculateTotal()}`) + } + + // 3. Violating SRP: Saves to DB + public saveToDatabase(): void { + console.log("Saving shopping cart to database...") + } +} + +// Equivalent of Java main +function main(): void { + const cart = new ShoppingCart() + + cart.addProduct(new Product("Laptop", 50000)) + cart.addProduct(new Product("Mouse", 2000)) + + cart.printInvoice() + cart.saveToDatabase() +} + +main() + +/** + * If a .ts file has no imports or exports, TypeScript treats it as a global script. + * Adding `export {}` marks the file as a module and prevents + * "Duplicate identifier 'ShoppingCart'" errors. + */ +export {} \ No newline at end of file