javascript

examples

examples.js⚑
// ╔══════════════════════════════════════════════════════════════════════════════╗
// β•‘                         MODULE DESIGN PATTERNS                                β•‘
// β•‘               Singleton, Factory, Facade, Dependency Injection                β•‘
// β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     MODULE DESIGN PATTERNS OVERVIEW                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Patterns covered:                                                              β”‚
β”‚                                                                                  β”‚
β”‚  1. Singleton Pattern      - Single instance across application                 β”‚
β”‚  2. Factory Pattern        - Create objects without specifying class            β”‚
β”‚  3. Facade Pattern         - Simplified interface to complex system             β”‚
β”‚  4. Observer Pattern       - Publish/Subscribe for loose coupling               β”‚
β”‚  5. Dependency Injection   - Decouple dependencies for testing                  β”‚
β”‚  6. Adapter Pattern        - Make incompatible interfaces work together         β”‚
β”‚  7. Strategy Pattern       - Interchangeable algorithms                         β”‚
β”‚  8. Repository Pattern     - Abstract data access                               β”‚
β”‚                                                                                  β”‚
β”‚  These patterns help create:                                                    β”‚
β”‚  β€’ Maintainable code                                                            β”‚
β”‚  β€’ Testable modules                                                             β”‚
β”‚  β€’ Loose coupling                                                               β”‚
β”‚  β€’ Clear interfaces                                                             β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

console.log('Module Design Patterns - Examples');
console.log('==================================\n');

// ════════════════════════════════════════════════════════════════════════════════
// 1. SINGLETON PATTERN
// ════════════════════════════════════════════════════════════════════════════════

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          SINGLETON PATTERN                                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Ensures a class has only ONE instance and provides global access               β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                 β”‚   β”‚
β”‚  β”‚    β”‚   Singleton      β”‚                                                 β”‚   β”‚
β”‚  β”‚    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                                                 β”‚   β”‚
β”‚  β”‚    β”‚ - instance       β”‚ ◄──── Only ONE instance exists                  β”‚   β”‚
β”‚  β”‚    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                                                 β”‚   β”‚
β”‚  β”‚    β”‚ + getInstance()  β”‚ ◄──── Global access point                       β”‚   β”‚
β”‚  β”‚    β”‚ + doSomething()  β”‚                                                 β”‚   β”‚
β”‚  β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                 β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                                  β”‚
β”‚  Use cases:                                                                     β”‚
β”‚  β€’ Configuration management                                                     β”‚
β”‚  β€’ Database connections                                                         β”‚
β”‚  β€’ Logging services                                                             β”‚
β”‚  β€’ Application state                                                            β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

// Method 1: ES Module Singleton (simplest - modules are singletons by default!)
const configSingleton = {
  apiUrl: 'https://api.example.com',
  debug: true,
  version: '1.0.0',

  get(key) {
    return this[key];
  },

  set(key, value) {
    this[key] = value;
  },
};

// When imported, this is always the SAME instance
// export default configSingleton;

// Method 2: Class-based Singleton
class DatabaseConnection {
  static #instance = null;

  constructor() {
    if (DatabaseConnection.#instance) {
      return DatabaseConnection.#instance;
    }

    this.connection = null;
    this.isConnected = false;
    DatabaseConnection.#instance = this;
  }

  static getInstance() {
    if (!DatabaseConnection.#instance) {
      DatabaseConnection.#instance = new DatabaseConnection();
    }
    return DatabaseConnection.#instance;
  }

  connect(connectionString) {
    if (this.isConnected) {
      console.log('  Already connected');
      return this;
    }

    console.log(`  Connecting to: ${connectionString}`);
    this.connection = connectionString;
    this.isConnected = true;
    return this;
  }

  disconnect() {
    this.isConnected = false;
    this.connection = null;
    console.log('  Disconnected');
    return this;
  }

  query(sql) {
    if (!this.isConnected) {
      throw new Error('Not connected');
    }
    console.log(`  Executing: ${sql}`);
    return { results: [] };
  }
}

// Method 3: Closure-based Singleton
const Logger = (function () {
  let instance = null;

  class LoggerClass {
    constructor() {
      this.logs = [];
      this.level = 'info';
    }

    setLevel(level) {
      this.level = level;
    }

    log(message) {
      const entry = { timestamp: new Date(), level: this.level, message };
      this.logs.push(entry);
      console.log(`  [${entry.level.toUpperCase()}] ${message}`);
    }

    getLogs() {
      return [...this.logs];
    }
  }

  return {
    getInstance() {
      if (!instance) {
        instance = new LoggerClass();
      }
      return instance;
    },
  };
})();

// Test Singleton
console.log('1. Singleton Pattern:');

// Database singleton
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log('  Same instance:', db1 === db2); // true

db1.connect('mongodb://localhost:27017');
db2.query('SELECT * FROM users'); // Works - same instance

// Logger singleton
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
logger1.log('First message');
console.log('  Logger same instance:', logger1 === logger2);
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// 2. FACTORY PATTERN
// ════════════════════════════════════════════════════════════════════════════════

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          FACTORY PATTERN                                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Creates objects without exposing the instantiation logic                       β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    Client ───► Factory ───┬───► ProductA                               β”‚   β”‚
β”‚  β”‚                           β”œβ”€β”€β”€β–Ί ProductB                               β”‚   β”‚
β”‚  β”‚                           └───► ProductC                               β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    Client doesn't know which class was instantiated                    β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                                  β”‚
β”‚  Use cases:                                                                     β”‚
β”‚  β€’ Creating different types of objects based on input                           β”‚
β”‚  β€’ Complex object creation logic                                                β”‚
β”‚  β€’ Decoupling object creation from usage                                        β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

// Example 1: Simple Factory
class UserFactory {
  static create(type, data) {
    switch (type) {
      case 'admin':
        return new AdminUser(data);
      case 'customer':
        return new CustomerUser(data);
      case 'guest':
        return new GuestUser(data);
      default:
        throw new Error(`Unknown user type: ${type}`);
    }
  }
}

class AdminUser {
  constructor({ name, email }) {
    this.name = name;
    this.email = email;
    this.role = 'admin';
    this.permissions = ['read', 'write', 'delete', 'manage'];
  }

  getInfo() {
    return `Admin: ${this.name} (${this.email})`;
  }
}

class CustomerUser {
  constructor({ name, email }) {
    this.name = name;
    this.email = email;
    this.role = 'customer';
    this.permissions = ['read', 'write'];
  }

  getInfo() {
    return `Customer: ${this.name} (${this.email})`;
  }
}

class GuestUser {
  constructor({ name = 'Guest' } = {}) {
    this.name = name;
    this.role = 'guest';
    this.permissions = ['read'];
  }

  getInfo() {
    return `Guest: ${this.name}`;
  }
}

// Example 2: Abstract Factory
const NotificationFactory = {
  // Factory for creating notifications based on channel
  createNotification(channel, message) {
    const factories = {
      email: () => ({
        type: 'email',
        message,
        send() {
          console.log(`    Email: ${message}`);
        },
      }),
      sms: () => ({
        type: 'sms',
        message,
        send() {
          console.log(`    SMS: ${message}`);
        },
      }),
      push: () => ({
        type: 'push',
        message,
        send() {
          console.log(`    Push: ${message}`);
        },
      }),
      slack: () => ({
        type: 'slack',
        message,
        send() {
          console.log(`    Slack: ${message}`);
        },
      }),
    };

    const factory = factories[channel];
    if (!factory) {
      throw new Error(`Unknown channel: ${channel}`);
    }

    return factory();
  },

  // Create multiple notifications at once
  createMultiple(channels, message) {
    return channels.map((channel) => this.createNotification(channel, message));
  },
};

// Test Factory
console.log('2. Factory Pattern:');

// User factory
const admin = UserFactory.create('admin', {
  name: 'John',
  email: 'john@admin.com',
});
const customer = UserFactory.create('customer', {
  name: 'Jane',
  email: 'jane@example.com',
});
const guest = UserFactory.create('guest');

console.log('  ' + admin.getInfo());
console.log('  ' + customer.getInfo());
console.log('  ' + guest.getInfo());

// Notification factory
console.log('  Sending notifications:');
const notifications = NotificationFactory.createMultiple(
  ['email', 'push'],
  'Hello!'
);
notifications.forEach((n) => n.send());
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// 3. FACADE PATTERN
// ════════════════════════════════════════════════════════════════════════════════

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          FACADE PATTERN                                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Provides a simplified interface to a complex subsystem                         β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    Client ───► Facade ───┬───► SubsystemA                              β”‚   β”‚
β”‚  β”‚                          β”œβ”€β”€β”€β–Ί SubsystemB                              β”‚   β”‚
β”‚  β”‚                          β”œβ”€β”€β”€β–Ί SubsystemC                              β”‚   β”‚
β”‚  β”‚                          └───► SubsystemD                              β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    Client only knows about Facade's simple interface                   β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                                  β”‚
β”‚  Use cases:                                                                     β”‚
β”‚  β€’ Simplifying complex APIs                                                     β”‚
β”‚  β€’ Wrapping legacy code                                                         β”‚
β”‚  β€’ Creating unified interface for multiple services                             β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

// Complex subsystems
class AuthService {
  login(email, password) {
    console.log('    AuthService: Authenticating...');
    return { userId: '123', token: 'abc123' };
  }

  logout(token) {
    console.log('    AuthService: Logging out...');
    return true;
  }

  validateToken(token) {
    return token && token.length > 0;
  }
}

class UserProfileService {
  getProfile(userId) {
    console.log('    UserProfileService: Fetching profile...');
    return { userId, name: 'John Doe', email: 'john@example.com' };
  }

  updateProfile(userId, data) {
    console.log('    UserProfileService: Updating profile...');
    return { ...data, userId };
  }
}

class PermissionService {
  getPermissions(userId) {
    console.log('    PermissionService: Getting permissions...');
    return ['read', 'write', 'delete'];
  }

  checkPermission(userId, permission) {
    return this.getPermissions(userId).includes(permission);
  }
}

class SessionService {
  createSession(userId, token) {
    console.log('    SessionService: Creating session...');
    return { sessionId: 'sess_123', userId, token, createdAt: new Date() };
  }

  destroySession(sessionId) {
    console.log('    SessionService: Destroying session...');
    return true;
  }
}

// Facade - simplified interface
class UserFacade {
  constructor() {
    this.authService = new AuthService();
    this.profileService = new UserProfileService();
    this.permissionService = new PermissionService();
    this.sessionService = new SessionService();
  }

  // Simple login that handles everything
  login(email, password) {
    console.log('  UserFacade.login():');

    // Authenticate
    const auth = this.authService.login(email, password);

    // Get profile
    const profile = this.profileService.getProfile(auth.userId);

    // Get permissions
    const permissions = this.permissionService.getPermissions(auth.userId);

    // Create session
    const session = this.sessionService.createSession(auth.userId, auth.token);

    // Return unified result
    return {
      user: profile,
      permissions,
      session: session.sessionId,
      token: auth.token,
    };
  }

  // Simple logout
  logout(sessionId, token) {
    console.log('  UserFacade.logout():');
    this.sessionService.destroySession(sessionId);
    this.authService.logout(token);
    return { success: true };
  }

  // Get user with all data
  getUser(userId) {
    const profile = this.profileService.getProfile(userId);
    const permissions = this.permissionService.getPermissions(userId);
    return { ...profile, permissions };
  }
}

// Test Facade
console.log('3. Facade Pattern:');
const userFacade = new UserFacade();
const loginResult = userFacade.login('john@example.com', 'password123');
console.log('  Login result:', {
  user: loginResult.user.name,
  permissions: loginResult.permissions.length,
});
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// 4. OBSERVER PATTERN (Pub/Sub)
// ════════════════════════════════════════════════════════════════════════════════

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     OBSERVER / PUB-SUB PATTERN                                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Defines a subscription mechanism for events                                    β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    Publisher ──emit──► EventBus ──notify──► Subscriber A               β”‚   β”‚
β”‚  β”‚                           β”‚                                             β”‚   β”‚
β”‚  β”‚                           β”œβ”€β”€β”€β”€β”€β”€β”€notify──► Subscriber B               β”‚   β”‚
β”‚  β”‚                           β”‚                                             β”‚   β”‚
β”‚  β”‚                           └───────notify──► Subscriber C               β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                                  β”‚
β”‚  Use cases:                                                                     β”‚
β”‚  β€’ Event-driven architecture                                                    β”‚
β”‚  β€’ Decoupled components                                                         β”‚
β”‚  β€’ UI state management                                                          β”‚
β”‚  β€’ Real-time updates                                                            β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

class EventBus {
  constructor() {
    this.events = new Map();
  }

  // Subscribe to an event
  on(event, callback) {
    if (!this.events.has(event)) {
      this.events.set(event, new Set());
    }
    this.events.get(event).add(callback);

    // Return unsubscribe function
    return () => this.off(event, callback);
  }

  // Subscribe once
  once(event, callback) {
    const wrapper = (...args) => {
      this.off(event, wrapper);
      callback(...args);
    };
    return this.on(event, wrapper);
  }

  // Unsubscribe
  off(event, callback) {
    const callbacks = this.events.get(event);
    if (callbacks) {
      callbacks.delete(callback);
    }
  }

  // Emit event
  emit(event, data) {
    const callbacks = this.events.get(event);
    if (callbacks) {
      callbacks.forEach((callback) => {
        try {
          callback(data);
        } catch (error) {
          console.error(`Error in event handler for ${event}:`, error);
        }
      });
    }
  }

  // Get subscriber count
  listenerCount(event) {
    return this.events.get(event)?.size || 0;
  }

  // Remove all listeners for an event
  removeAllListeners(event) {
    if (event) {
      this.events.delete(event);
    } else {
      this.events.clear();
    }
  }
}

// Global event bus (singleton)
const eventBus = new EventBus();

// Example usage: Shopping cart with events
class ShoppingCart {
  constructor(eventBus) {
    this.items = [];
    this.eventBus = eventBus;
  }

  addItem(item) {
    this.items.push(item);
    this.eventBus.emit('cart:itemAdded', { item, cart: this });
    this.eventBus.emit('cart:updated', {
      items: this.items,
      total: this.getTotal(),
    });
  }

  removeItem(itemId) {
    const index = this.items.findIndex((i) => i.id === itemId);
    if (index > -1) {
      const item = this.items.splice(index, 1)[0];
      this.eventBus.emit('cart:itemRemoved', { item, cart: this });
      this.eventBus.emit('cart:updated', {
        items: this.items,
        total: this.getTotal(),
      });
    }
  }

  getTotal() {
    return this.items.reduce((sum, item) => sum + item.price, 0);
  }
}

// Test Observer
console.log('4. Observer Pattern:');

const cart = new ShoppingCart(eventBus);

// Subscribe to events
const unsubscribe = eventBus.on('cart:itemAdded', ({ item }) => {
  console.log(`  Item added: ${item.name} - $${item.price}`);
});

eventBus.on('cart:updated', ({ total }) => {
  console.log(`  Cart total: $${total}`);
});

// Add items
cart.addItem({ id: 1, name: 'Laptop', price: 999 });
cart.addItem({ id: 2, name: 'Mouse', price: 29 });

// Unsubscribe from one event
unsubscribe();
cart.addItem({ id: 3, name: 'Keyboard', price: 79 }); // Won't log "Item added"
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// 5. DEPENDENCY INJECTION PATTERN
// ════════════════════════════════════════════════════════════════════════════════

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     DEPENDENCY INJECTION PATTERN                                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Inject dependencies instead of creating them inside the class                  β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    ❌ Without DI:           βœ“ With DI:                                  β”‚   β”‚
β”‚  β”‚    class Service {         class Service {                              β”‚   β”‚
β”‚  β”‚      db = new Database();    constructor(db) {                          β”‚   β”‚
β”‚  β”‚    }                           this.db = db;                            β”‚   β”‚
β”‚  β”‚                              }                                          β”‚   β”‚
β”‚  β”‚    Hard to test!           }                                            β”‚   β”‚
β”‚  β”‚                              Easy to mock!                              β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                                  β”‚
β”‚  Benefits:                                                                      β”‚
β”‚  β€’ Easier testing (inject mocks)                                                β”‚
β”‚  β€’ Loose coupling                                                               β”‚
β”‚  β€’ Flexibility to swap implementations                                          β”‚
β”‚  β€’ Better separation of concerns                                                β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

// Simple DI Container
class DIContainer {
  constructor() {
    this.services = new Map();
    this.singletons = new Map();
  }

  // Register a service
  register(name, factory, options = {}) {
    this.services.set(name, {
      factory,
      singleton: options.singleton || false,
    });
    return this;
  }

  // Resolve a service
  resolve(name) {
    const service = this.services.get(name);
    if (!service) {
      throw new Error(`Service not found: ${name}`);
    }

    // Return singleton if exists
    if (service.singleton && this.singletons.has(name)) {
      return this.singletons.get(name);
    }

    // Create instance
    const instance = service.factory(this);

    // Cache singleton
    if (service.singleton) {
      this.singletons.set(name, instance);
    }

    return instance;
  }

  // Check if service exists
  has(name) {
    return this.services.has(name);
  }
}

// Example: Service with dependencies
class EmailService {
  send(to, subject, body) {
    console.log(`    Sending email to ${to}: ${subject}`);
    return { sent: true, to, subject };
  }
}

class LoggerService {
  log(message) {
    console.log(`    [LOG] ${message}`);
  }
}

// Service that depends on others
class OrderService {
  constructor(emailService, logger) {
    this.emailService = emailService;
    this.logger = logger;
  }

  placeOrder(order) {
    this.logger.log(`Processing order ${order.id}`);

    // Process order...

    this.emailService.send(
      order.customerEmail,
      'Order Confirmation',
      `Your order ${order.id} has been placed!`
    );

    this.logger.log(`Order ${order.id} completed`);
    return { success: true, orderId: order.id };
  }
}

// Setup DI container
const container = new DIContainer();

container
  .register('logger', () => new LoggerService(), { singleton: true })
  .register('email', () => new EmailService(), { singleton: true })
  .register(
    'orderService',
    (c) => new OrderService(c.resolve('email'), c.resolve('logger'))
  );

// Test DI
console.log('5. Dependency Injection:');

const orderService = container.resolve('orderService');
orderService.placeOrder({
  id: 'ORD-001',
  customerEmail: 'customer@example.com',
  items: [{ name: 'Widget', quantity: 2 }],
});

// For testing, you can inject mocks:
console.log('  With mock (testing):');
const mockEmail = { send: () => console.log('    Mock email sent!') };
const mockLogger = { log: (msg) => console.log(`    [MOCK LOG] ${msg}`) };
const testOrderService = new OrderService(mockEmail, mockLogger);
testOrderService.placeOrder({ id: 'TEST-001', customerEmail: 'test@test.com' });
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// 6. ADAPTER PATTERN
// ════════════════════════════════════════════════════════════════════════════════

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          ADAPTER PATTERN                                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Makes incompatible interfaces work together                                    β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    Client ───► Adapter ───► Adaptee                                    β”‚   β”‚
β”‚  β”‚                  β”‚                                                      β”‚   β”‚
β”‚  β”‚          Translates between interfaces                                  β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                                  β”‚
β”‚  Use cases:                                                                     β”‚
β”‚  β€’ Integrating third-party APIs                                                 β”‚
β”‚  β€’ Legacy code integration                                                      β”‚
β”‚  β€’ Standardizing different implementations                                      β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

// Old API (legacy)
class OldPaymentSystem {
  processPayment(amount, cardNumber, cvv, expiry) {
    console.log(`    OldPayment: Processing $${amount}`);
    return { status: 'success', transactionId: 'OLD-' + Date.now() };
  }
}

// New API (expected interface)
// interface PaymentProcessor {
//     pay(paymentDetails: PaymentDetails): PaymentResult
// }

// Adapter to make old system work with new interface
class PaymentAdapter {
  constructor(oldSystem) {
    this.oldSystem = oldSystem;
  }

  // New interface method
  pay(paymentDetails) {
    const { amount, card } = paymentDetails;

    // Adapt to old interface
    const result = this.oldSystem.processPayment(
      amount,
      card.number,
      card.cvv,
      card.expiry
    );

    // Adapt response to new format
    return {
      success: result.status === 'success',
      transactionId: result.transactionId,
      amount: amount,
    };
  }
}

// Another adapter for a different payment provider
class StripeAdapter {
  constructor() {
    // Would connect to Stripe API
  }

  pay(paymentDetails) {
    console.log(`    Stripe: Processing $${paymentDetails.amount}`);
    return {
      success: true,
      transactionId: 'STRIPE-' + Date.now(),
      amount: paymentDetails.amount,
    };
  }
}

// Test Adapter
console.log('6. Adapter Pattern:');

// Use old system through adapter
const oldSystem = new OldPaymentSystem();
const paymentProcessor = new PaymentAdapter(oldSystem);

const result = paymentProcessor.pay({
  amount: 99.99,
  card: { number: '4111111111111111', cvv: '123', expiry: '12/25' },
});
console.log('  Payment result:', result);

// Can easily swap to different adapter
const stripeProcessor = new StripeAdapter();
const stripeResult = stripeProcessor.pay({ amount: 49.99 });
console.log('  Stripe result:', stripeResult);
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// 7. STRATEGY PATTERN
// ════════════════════════════════════════════════════════════════════════════════

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          STRATEGY PATTERN                                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Define family of algorithms and make them interchangeable                      β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    Context ───► Strategy Interface                                     β”‚   β”‚
β”‚  β”‚                      β–²                                                  β”‚   β”‚
β”‚  β”‚          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                     β”‚   β”‚
β”‚  β”‚          β”‚           β”‚           β”‚                                     β”‚   β”‚
β”‚  β”‚    StrategyA    StrategyB    StrategyC                                 β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                                  β”‚
β”‚  Use cases:                                                                     β”‚
β”‚  β€’ Different sorting algorithms                                                 β”‚
β”‚  β€’ Payment methods                                                              β”‚
β”‚  β€’ Compression algorithms                                                       β”‚
β”‚  β€’ Validation strategies                                                        β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

// Shipping strategies
const shippingStrategies = {
  standard: {
    name: 'Standard Shipping',
    calculate(weight, distance) {
      return weight * 0.5 + distance * 0.1;
    },
    estimatedDays: 7,
  },

  express: {
    name: 'Express Shipping',
    calculate(weight, distance) {
      return weight * 1.0 + distance * 0.3 + 10;
    },
    estimatedDays: 3,
  },

  overnight: {
    name: 'Overnight Shipping',
    calculate(weight, distance) {
      return weight * 2.0 + distance * 0.5 + 25;
    },
    estimatedDays: 1,
  },

  pickup: {
    name: 'Store Pickup',
    calculate() {
      return 0;
    },
    estimatedDays: 0,
  },
};

// Context that uses strategies
class ShippingCalculator {
  constructor(strategy = shippingStrategies.standard) {
    this.strategy = strategy;
  }

  setStrategy(strategy) {
    this.strategy = strategy;
  }

  calculate(order) {
    const weight = order.items.reduce((sum, item) => sum + item.weight, 0);
    const distance = order.distance || 100;

    const cost = this.strategy.calculate(weight, distance);

    return {
      method: this.strategy.name,
      cost: cost.toFixed(2),
      estimatedDays: this.strategy.estimatedDays,
    };
  }
}

// Test Strategy
console.log('7. Strategy Pattern:');

const order = {
  items: [
    { name: 'Laptop', weight: 3 },
    { name: 'Mouse', weight: 0.2 },
  ],
  distance: 150,
};

const calculator = new ShippingCalculator();

// Try different strategies
Object.entries(shippingStrategies).forEach(([key, strategy]) => {
  calculator.setStrategy(strategy);
  const result = calculator.calculate(order);
  console.log(
    `  ${result.method}: $${result.cost} (${result.estimatedDays} days)`
  );
});
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// 8. REPOSITORY PATTERN
// ════════════════════════════════════════════════════════════════════════════════

/*
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         REPOSITORY PATTERN                                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                                  β”‚
β”‚  Abstracts data access behind a collection-like interface                       β”‚
β”‚                                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β”‚    Business Logic ───► Repository Interface                            β”‚   β”‚
β”‚  β”‚                              β–²                                         β”‚   β”‚
β”‚  β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                         β”‚   β”‚
β”‚  β”‚              β”‚               β”‚               β”‚                         β”‚   β”‚
β”‚  β”‚    SQLRepository    MongoRepository    InMemoryRepository              β”‚   β”‚
β”‚  β”‚                                                                         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                                  β”‚
β”‚  Benefits:                                                                      β”‚
β”‚  β€’ Decouple business logic from data access                                     β”‚
β”‚  β€’ Easy to swap storage implementations                                         β”‚
β”‚  β€’ Easier to test with in-memory repository                                     β”‚
β”‚                                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
*/

// Repository interface (conceptual)
class Repository {
  findById(id) {
    throw new Error('Not implemented');
  }
  findAll() {
    throw new Error('Not implemented');
  }
  create(entity) {
    throw new Error('Not implemented');
  }
  update(id, entity) {
    throw new Error('Not implemented');
  }
  delete(id) {
    throw new Error('Not implemented');
  }
}

// In-memory implementation (for testing/development)
class InMemoryUserRepository extends Repository {
  constructor() {
    super();
    this.users = new Map();
    this.nextId = 1;
  }

  findById(id) {
    return this.users.get(id) || null;
  }

  findAll() {
    return Array.from(this.users.values());
  }

  findByEmail(email) {
    return this.findAll().find((u) => u.email === email);
  }

  create(userData) {
    const user = {
      id: this.nextId++,
      ...userData,
      createdAt: new Date(),
    };
    this.users.set(user.id, user);
    return user;
  }

  update(id, userData) {
    const user = this.users.get(id);
    if (!user) return null;

    const updated = { ...user, ...userData, updatedAt: new Date() };
    this.users.set(id, updated);
    return updated;
  }

  delete(id) {
    return this.users.delete(id);
  }
}

// API implementation (for production)
class ApiUserRepository extends Repository {
  constructor(apiUrl) {
    super();
    this.apiUrl = apiUrl;
  }

  async findById(id) {
    // Would make API call
    console.log(`    API: GET ${this.apiUrl}/users/${id}`);
    return { id, name: 'API User' };
  }

  async findAll() {
    console.log(`    API: GET ${this.apiUrl}/users`);
    return [];
  }

  async create(userData) {
    console.log(`    API: POST ${this.apiUrl}/users`);
    return { id: Date.now(), ...userData };
  }

  async update(id, userData) {
    console.log(`    API: PUT ${this.apiUrl}/users/${id}`);
    return { id, ...userData };
  }

  async delete(id) {
    console.log(`    API: DELETE ${this.apiUrl}/users/${id}`);
    return true;
  }
}

// Service using repository
class UserService {
  constructor(userRepository) {
    this.repo = userRepository;
  }

  getUser(id) {
    return this.repo.findById(id);
  }

  createUser(data) {
    // Business logic here
    if (!data.email) {
      throw new Error('Email is required');
    }
    return this.repo.create(data);
  }

  getAllUsers() {
    return this.repo.findAll();
  }
}

// Test Repository
console.log('8. Repository Pattern:');

// Use in-memory for testing
const memoryRepo = new InMemoryUserRepository();
const userService = new UserService(memoryRepo);

const user1 = userService.createUser({
  name: 'John',
  email: 'john@example.com',
});
const user2 = userService.createUser({
  name: 'Jane',
  email: 'jane@example.com',
});

console.log(
  '  Created users:',
  userService
    .getAllUsers()
    .map((u) => u.name)
    .join(', ')
);
console.log('  Find by ID:', userService.getUser(1)?.name);

// For production, swap to API repository
console.log('  API Repository:');
const apiRepo = new ApiUserRepository('https://api.example.com');
const prodService = new UserService(apiRepo);
prodService.getUser(1);
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// SUMMARY
// ════════════════════════════════════════════════════════════════════════════════

console.log(`
╔══════════════════════════════════════════════════════════════════════════════╗
β•‘                    MODULE DESIGN PATTERNS - COMPLETE                         β•‘
╠══════════════════════════════════════════════════════════════════════════════╣
β•‘                                                                              β•‘
β•‘  Patterns Covered:                                                           β•‘
β•‘                                                                              β•‘
β•‘  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β•‘
β•‘  β”‚  Pattern        β”‚  Purpose                                            β”‚  β•‘
β•‘  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β•‘
β•‘  β”‚  Singleton      β”‚  Single instance, global access                     β”‚  β•‘
β•‘  β”‚  Factory        β”‚  Create objects without specifying class            β”‚  β•‘
β•‘  β”‚  Facade         β”‚  Simplified interface to complex system             β”‚  β•‘
β•‘  β”‚  Observer       β”‚  Event-driven communication                         β”‚  β•‘
β•‘  β”‚  DI             β”‚  Inject dependencies for testing                    β”‚  β•‘
β•‘  β”‚  Adapter        β”‚  Make incompatible interfaces work                  β”‚  β•‘
β•‘  β”‚  Strategy       β”‚  Interchangeable algorithms                         β”‚  β•‘
β•‘  β”‚  Repository     β”‚  Abstract data access                               β”‚  β•‘
β•‘  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β•‘
β•‘                                                                              β•‘
β•‘  When to Use:                                                                β•‘
β•‘  β€’ Singleton: Config, DB connections, logging                               β•‘
β•‘  β€’ Factory: Creating different types based on input                         β•‘
β•‘  β€’ Facade: Simplifying complex APIs                                         β•‘
β•‘  β€’ Observer: Decoupled event handling                                       β•‘
β•‘  β€’ DI: Testable, loosely-coupled code                                       β•‘
β•‘  β€’ Adapter: Third-party integrations                                        β•‘
β•‘  β€’ Strategy: Swappable algorithms                                           β•‘
β•‘  β€’ Repository: Data access abstraction                                      β•‘
β•‘                                                                              β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
`);
Examples - JavaScript Tutorial | DeepML