Software Design Patterns

Contents

Creational Patterns

Singleton Pattern

// TypeScript Singleton Example class DatabaseConnection { private static instance: DatabaseConnection; private constructor() { // Private constructor to prevent direct instantiation } public static getInstance(): DatabaseConnection { if (!DatabaseConnection.instance) { DatabaseConnection.instance = new DatabaseConnection(); } return DatabaseConnection.instance; } public query(sql: string): void { console.log(`Executing query: ${sql}`); } } // Usage const db1 = DatabaseConnection.getInstance(); const db2 = DatabaseConnection.getInstance(); console.log(db1 === db2); // true

Factory Pattern

// Factory Method Pattern interface Animal { speak(): string; } class Dog implements Animal { speak(): string { return "Woof!"; } } class Cat implements Animal { speak(): string { return "Meow!"; } } class AnimalFactory { createAnimal(type: string): Animal { switch (type.toLowerCase()) { case "dog": return new Dog(); case "cat": return new Cat(); default: throw new Error("Unknown animal type"); } } } // Usage const factory = new AnimalFactory(); const dog = factory.createAnimal("dog"); const cat = factory.createAnimal("cat");

Structural Patterns

Adapter Pattern

// Adapter Pattern Example interface ModernPaymentGateway { processPayment(amount: number): void; } class LegacyPaymentSystem { makePayment(sum: number, currency: string): void { console.log(`Processing ${sum} ${currency}`); } } class PaymentAdapter implements ModernPaymentGateway { private legacySystem: LegacyPaymentSystem; constructor(legacySystem: LegacyPaymentSystem) { this.legacySystem = legacySystem; } processPayment(amount: number): void { this.legacySystem.makePayment(amount, "USD"); } }

Decorator Pattern

// Decorator Pattern Example interface Coffee { cost(): number; description(): string; } class SimpleCoffee implements Coffee { cost(): number { return 10; } description(): string { return "Simple coffee"; } } abstract class CoffeeDecorator implements Coffee { protected coffee: Coffee; constructor(coffee: Coffee) { this.coffee = coffee; } cost(): number { return this.coffee.cost(); } description(): string { return this.coffee.description(); } } class MilkDecorator extends CoffeeDecorator { cost(): number { return this.coffee.cost() + 2; } description(): string { return `${this.coffee.description()}, with milk`; } }

Behavioral Patterns

Observer Pattern

// Observer Pattern Example interface Observer { update(data: any): void; } class Subject { private observers: Observer[] = []; public addObserver(observer: Observer): void { this.observers.push(observer); } public removeObserver(observer: Observer): void { const index = this.observers.indexOf(observer); if (index > -1) { this.observers.splice(index, 1); } } public notify(data: any): void { this.observers.forEach(observer => observer.update(data)); } } class DataSource extends Subject { private data: string = ""; public setData(data: string): void { this.data = data; this.notify(data); } }

Strategy Pattern

// Strategy Pattern Example interface PaymentStrategy { pay(amount: number): void; } class CreditCardPayment implements PaymentStrategy { private cardNumber: string; private cvv: string; constructor(cardNumber: string, cvv: string) { this.cardNumber = cardNumber; this.cvv = cvv; } pay(amount: number): void { console.log(`Paying ${amount} using Credit Card`); } } class PayPalPayment implements PaymentStrategy { private email: string; constructor(email: string) { this.email = email; } pay(amount: number): void { console.log(`Paying ${amount} using PayPal`); } }

Architectural Patterns

MVC Pattern

// MVC Pattern Example class UserModel { private name: string; private email: string; constructor(name: string, email: string) { this.name = name; this.email = email; } getName(): string { return this.name; } getEmail(): string { return this.email; } } class UserView { display(name: string, email: string): void { console.log(`User: ${name}`); console.log(`Email: ${email}`); } } class UserController { private model: UserModel; private view: UserView; constructor(model: UserModel, view: UserView) { this.model = model; this.view = view; } updateView(): void { this.view.display( this.model.getName(), this.model.getEmail() ); } }

Repository Pattern

// Repository Pattern Example interface UserRepository { findById(id: number): Promise; save(user: User): Promise; delete(id: number): Promise; } class PostgresUserRepository implements UserRepository { async findById(id: number): Promise { // Implementation for PostgreSQL return new User(); } async save(user: User): Promise { // Implementation for PostgreSQL } async delete(id: number): Promise { // Implementation for PostgreSQL } }

Anti-patterns

Common Anti-patterns

  • God Object
  • Spaghetti Code
  • Golden Hammer
  • Copy-Paste Programming
  • Hard Coding
  • Magic Numbers

Examples to Avoid

// God Object Anti-pattern class UserManager { // Too many responsibilities in one class private users: User[] = []; private database: Database; private logger: Logger; private emailService: EmailService; private paymentProcessor: PaymentProcessor; constructor() { this.database = new Database(); this.logger = new Logger(); this.emailService = new EmailService(); this.paymentProcessor = new PaymentProcessor(); } // Methods that should be in separate classes createUser() { /* ... */ } validateUser() { /* ... */ } sendEmail() { /* ... */ } processPayment() { /* ... */ } generateReport() { /* ... */ } backupData() { /* ... */ } // ... many more unrelated methods }