+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 43 of 72

🎯 Interface Segregation: SOLID Principles in Practice

Master the Interface Segregation Principle (ISP) in TypeScript to create focused, maintainable interfaces that follow SOLID design principles 🚀

💎Advanced
25 min read

Prerequisites

  • Understanding of interfaces 📝
  • Knowledge of inheritance 🔍
  • Basic SOLID principles awareness 💻

What you'll learn

  • Understand Interface Segregation Principle 🎯
  • Identify and fix interface pollution 🏗️
  • Design cohesive, focused interfaces 🛡️
  • Apply ISP in real-world scenarios ✨

🎯 Introduction

Welcome to the world of clean interface design! 🎉 In this guide, we’ll explore the Interface Segregation Principle (ISP), one of the five SOLID principles that helps create maintainable, flexible software architectures.

You’ll discover how ISP is like a well-organized toolbox 🧰 - instead of one massive Swiss Army knife that does everything, you have specific tools for specific jobs. Whether you’re designing APIs 🌐, building component libraries 📚, or creating service layers 🏗️, understanding ISP is crucial for writing professional-grade TypeScript code.

By the end of this tutorial, you’ll be confidently designing interfaces that are focused, cohesive, and a joy to work with! Let’s segregate! 🏊‍♂️

📚 Understanding Interface Segregation Principle

🤔 What is ISP?

The Interface Segregation Principle states: “Clients should not be forced to depend on interfaces they don’t use.” In simpler terms, it’s better to have many small, focused interfaces than one large, all-encompassing interface.

Think of it like a restaurant menu 🍽️:

  • ❌ Bad: One giant menu with breakfast, lunch, dinner, desserts, and drinks all mixed together
  • ✅ Good: Separate menus for breakfast, lunch, dinner, with focused options

💡 Why ISP Matters

Here’s why developers embrace ISP:

  1. Flexibility 🤸‍♂️: Classes implement only what they need
  2. Maintainability 🔧: Changes to one interface don’t affect unrelated code
  3. Testability 🧪: Easier to mock and test focused interfaces
  4. Clarity 🎯: Clear contracts and responsibilities

Real-world example: Imagine a printer interface 🖨️. Not all printers can scan, fax, or staple. Following ISP, we’d have separate interfaces for Printable, Scannable, Faxable, and Stapleable, allowing each printer to implement only what it actually supports.

🔧 Identifying Interface Pollution

📝 Signs of a Polluted Interface

Let’s start by recognizing when interfaces violate ISP:

// ❌ BAD: Fat interface with too many responsibilities
interface Employee {
  // Personal info
  id: string;
  name: string;
  email: string;
  phone: string;
  address: string;
  
  // Work info
  department: string;
  position: string;
  salary: number;
  startDate: Date;
  
  // Methods for ALL employees
  work(): void;
  attendMeeting(): void;
  submitTimesheet(): void;
  
  // Manager-specific methods
  approveTimesheet(employeeId: string): void;
  conductPerformanceReview(employeeId: string): void;
  assignTask(employeeId: string, task: string): void;
  
  // Developer-specific methods
  writeCode(): void;
  reviewCode(pullRequestId: string): void;
  deployApplication(): void;
  
  // HR-specific methods
  processPayroll(): void;
  handleBenefits(): void;
  recruitNewEmployee(): void;
  
  // Accounting-specific methods
  generateFinancialReport(): void;
  processExpenseReport(reportId: string): void;
  auditDepartmentBudget(): void;
}

// 😱 Now every employee type must implement ALL methods!
class Developer implements Employee {
  // ... all personal and work properties ...
  
  work() { console.log('Writing code...'); }
  attendMeeting() { console.log('Attending standup...'); }
  submitTimesheet() { console.log('Submitting hours...'); }
  
  // ❌ Forced to implement manager methods
  approveTimesheet(employeeId: string) {
    throw new Error('Developers cannot approve timesheets!');
  }
  conductPerformanceReview(employeeId: string) {
    throw new Error('Developers cannot conduct reviews!');
  }
  assignTask(employeeId: string, task: string) {
    throw new Error('Developers cannot assign tasks!');
  }
  
  // ✅ Developer methods make sense
  writeCode() { console.log('Writing TypeScript...'); }
  reviewCode(pullRequestId: string) { console.log('Reviewing PR...'); }
  deployApplication() { console.log('Deploying to production...'); }
  
  // ❌ Forced to implement HR methods
  processPayroll() {
    throw new Error('Developers cannot process payroll!');
  }
  handleBenefits() {
    throw new Error('Developers cannot handle benefits!');
  }
  recruitNewEmployee() {
    throw new Error('Developers cannot recruit!');
  }
  
  // ❌ Forced to implement accounting methods
  generateFinancialReport() {
    throw new Error('Developers cannot generate financial reports!');
  }
  processExpenseReport(reportId: string) {
    throw new Error('Developers cannot process expenses!');
  }
  auditDepartmentBudget() {
    throw new Error('Developers cannot audit budgets!');
  }
}

// 🤯 This is a nightmare to maintain and use!

🚨 Problems with Fat Interfaces

  1. Unused Methods 🗑️: Classes implement methods they’ll never use
  2. Coupling 🔗: Changes affect unrelated classes
  3. Confusion 😵: Unclear what each class actually does
  4. Testing Nightmare 🧪: Must mock/stub unnecessary methods

🏗️ Applying Interface Segregation

✨ The Solution: Segregated Interfaces

Let’s refactor using ISP:

// ✅ GOOD: Segregated interfaces following ISP

// 🎯 Core employee information
interface Person {
  id: string;
  name: string;
  email: string;
  phone: string;
  address: string;
}

// 💼 Basic employee behavior
interface Employee extends Person {
  department: string;
  position: string;
  salary: number;
  startDate: Date;
  
  work(): void;
  attendMeeting(): void;
  submitTimesheet(): void;
}

// 👔 Manager capabilities
interface Manager {
  approveTimesheet(employeeId: string): void;
  conductPerformanceReview(employeeId: string): void;
  assignTask(employeeId: string, task: string): void;
  manageBudget(amount: number): void;
}

// 💻 Developer capabilities
interface Developer {
  writeCode(language: string): void;
  reviewCode(pullRequestId: string): void;
  debugApplication(issueId: string): void;
}

// 🚀 Deployment capabilities
interface Deployer {
  deployApplication(version: string): void;
  rollbackDeployment(version: string): void;
  monitorDeployment(): void;
}

// 👥 HR capabilities
interface HRSpecialist {
  processPayroll(): void;
  handleBenefits(employeeId: string): void;
  recruitNewEmployee(position: string): void;
  conductOnboarding(employeeId: string): void;
}

// 💰 Accounting capabilities
interface Accountant {
  generateFinancialReport(period: string): void;
  processExpenseReport(reportId: string): void;
  auditDepartmentBudget(department: string): void;
  forecastBudget(quarters: number): void;
}

// 🎨 Now we can compose employee types with only what they need!

// 💻 Software Developer - implements only relevant interfaces
class SoftwareDeveloper implements Employee, Developer {
  // Employee properties
  id: string;
  name: string;
  email: string;
  phone: string;
  address: string;
  department: string;
  position: string;
  salary: number;
  startDate: Date;
  
  constructor(personalInfo: Person, workInfo: Partial<Employee>) {
    Object.assign(this, personalInfo, workInfo);
  }
  
  // Employee methods
  work(): void {
    console.log(`${this.name} is coding...`);
  }
  
  attendMeeting(): void {
    console.log(`${this.name} is in a standup meeting...`);
  }
  
  submitTimesheet(): void {
    console.log(`${this.name} submitted timesheet`);
  }
  
  // Developer methods
  writeCode(language: string): void {
    console.log(`${this.name} is writing ${language} code`);
  }
  
  reviewCode(pullRequestId: string): void {
    console.log(`${this.name} is reviewing PR #${pullRequestId}`);
  }
  
  debugApplication(issueId: string): void {
    console.log(`${this.name} is debugging issue #${issueId}`);
  }
}

// 🚀 Senior Developer with deployment permissions
class SeniorDeveloper extends SoftwareDeveloper implements Deployer {
  deployApplication(version: string): void {
    console.log(`${this.name} is deploying version ${version}`);
  }
  
  rollbackDeployment(version: string): void {
    console.log(`${this.name} is rolling back to version ${version}`);
  }
  
  monitorDeployment(): void {
    console.log(`${this.name} is monitoring deployment metrics`);
  }
}

// 👔 Engineering Manager
class EngineeringManager implements Employee, Manager, Developer {
  // Properties
  id: string;
  name: string;
  email: string;
  phone: string;
  address: string;
  department: string;
  position: string;
  salary: number;
  startDate: Date;
  teamSize: number;
  
  constructor(personalInfo: Person, workInfo: Partial<Employee>, teamSize: number) {
    Object.assign(this, personalInfo, workInfo);
    this.teamSize = teamSize;
  }
  
  // Employee methods
  work(): void {
    console.log(`${this.name} is managing the team...`);
  }
  
  attendMeeting(): void {
    console.log(`${this.name} is in a leadership meeting...`);
  }
  
  submitTimesheet(): void {
    console.log(`${this.name} submitted managerial timesheet`);
  }
  
  // Manager methods
  approveTimesheet(employeeId: string): void {
    console.log(`${this.name} approved timesheet for employee ${employeeId}`);
  }
  
  conductPerformanceReview(employeeId: string): void {
    console.log(`${this.name} is reviewing employee ${employeeId}`);
  }
  
  assignTask(employeeId: string, task: string): void {
    console.log(`${this.name} assigned "${task}" to employee ${employeeId}`);
  }
  
  manageBudget(amount: number): void {
    console.log(`${this.name} is managing budget of $${amount}`);
  }
  
  // Developer methods (managers can still code!)
  writeCode(language: string): void {
    console.log(`${this.name} is helping with ${language} code`);
  }
  
  reviewCode(pullRequestId: string): void {
    console.log(`${this.name} is reviewing PR #${pullRequestId} for architecture`);
  }
  
  debugApplication(issueId: string): void {
    console.log(`${this.name} is helping debug critical issue #${issueId}`);
  }
}

// 👥 HR Manager
class HRManager implements Employee, Manager, HRSpecialist {
  // Properties
  id: string;
  name: string;
  email: string;
  phone: string;
  address: string;
  department: string = 'Human Resources';
  position: string;
  salary: number;
  startDate: Date;
  
  constructor(personalInfo: Person, workInfo: Partial<Employee>) {
    Object.assign(this, personalInfo, workInfo);
  }
  
  // Employee methods
  work(): void {
    console.log(`${this.name} is working on HR tasks...`);
  }
  
  attendMeeting(): void {
    console.log(`${this.name} is in an HR meeting...`);
  }
  
  submitTimesheet(): void {
    console.log(`${this.name} submitted HR timesheet`);
  }
  
  // Manager methods
  approveTimesheet(employeeId: string): void {
    console.log(`${this.name} approved timesheet for ${employeeId}`);
  }
  
  conductPerformanceReview(employeeId: string): void {
    console.log(`${this.name} is conducting HR review for ${employeeId}`);
  }
  
  assignTask(employeeId: string, task: string): void {
    console.log(`${this.name} assigned HR task "${task}" to ${employeeId}`);
  }
  
  manageBudget(amount: number): void {
    console.log(`${this.name} is managing HR budget of $${amount}`);
  }
  
  // HR methods
  processPayroll(): void {
    console.log(`${this.name} is processing company payroll`);
  }
  
  handleBenefits(employeeId: string): void {
    console.log(`${this.name} is updating benefits for ${employeeId}`);
  }
  
  recruitNewEmployee(position: string): void {
    console.log(`${this.name} is recruiting for ${position} position`);
  }
  
  conductOnboarding(employeeId: string): void {
    console.log(`${this.name} is onboarding new employee ${employeeId}`);
  }
}

🎨 Real-World Examples

🖨️ Printer System

Let’s design a printer system following ISP:

// 🖨️ Segregated printer interfaces

interface Printer {
  print(document: Document): void;
  getPrinterStatus(): PrinterStatus;
  getInkLevel(): number;
}

interface Scanner {
  scan(settings: ScanSettings): ScannedDocument;
  getScannerStatus(): ScannerStatus;
}

interface Fax {
  sendFax(number: string, document: Document): void;
  receiveFax(): Document | null;
  getFaxHistory(): FaxRecord[];
}

interface Copier {
  copy(document: Document, copies: number): void;
  getCopierStatus(): CopierStatus;
}

interface Stapler {
  staple(pages: number): void;
  getStaplerStatus(): StaplerStatus;
  refillStaples(): void;
}

interface NetworkDevice {
  connect(network: string, password: string): void;
  disconnect(): void;
  getNetworkStatus(): NetworkStatus;
  getIPAddress(): string;
}

interface CloudEnabled {
  uploadToCloud(document: Document, service: string): void;
  downloadFromCloud(documentId: string, service: string): Document;
  linkCloudAccount(service: string, credentials: any): void;
}

// 📄 Supporting types
interface Document {
  id: string;
  name: string;
  content: string;
  pages: number;
}

interface ScannedDocument extends Document {
  resolution: number;
  colorMode: 'color' | 'grayscale' | 'blackwhite';
}

interface ScanSettings {
  resolution: number;
  colorMode: 'color' | 'grayscale' | 'blackwhite';
  format: 'pdf' | 'jpeg' | 'png';
}

interface FaxRecord {
  id: string;
  number: string;
  timestamp: Date;
  pages: number;
  status: 'sent' | 'received' | 'failed';
}

type PrinterStatus = 'ready' | 'printing' | 'paper_jam' | 'out_of_paper' | 'offline';
type ScannerStatus = 'ready' | 'scanning' | 'warming_up' | 'error';
type CopierStatus = 'ready' | 'copying' | 'paper_jam' | 'maintenance';
type StaplerStatus = 'ready' | 'low_staples' | 'empty' | 'jammed';
type NetworkStatus = 'connected' | 'connecting' | 'disconnected' | 'error';

// 🖨️ Basic printer implementation
class BasicPrinter implements Printer {
  private inkLevel: number = 100;
  private status: PrinterStatus = 'ready';
  
  print(document: Document): void {
    console.log(`🖨️ Printing "${document.name}" (${document.pages} pages)`);
    this.inkLevel -= document.pages * 0.5;
    this.status = 'printing';
    
    setTimeout(() => {
      this.status = 'ready';
      console.log('✅ Printing complete!');
    }, 2000);
  }
  
  getPrinterStatus(): PrinterStatus {
    return this.status;
  }
  
  getInkLevel(): number {
    return Math.max(0, this.inkLevel);
  }
}

// 🖨️ Multifunction printer
class MultifunctionPrinter implements Printer, Scanner, Copier, NetworkDevice {
  private inkLevel: number = 100;
  private printerStatus: PrinterStatus = 'ready';
  private scannerStatus: ScannerStatus = 'ready';
  private copierStatus: CopierStatus = 'ready';
  private networkStatus: NetworkStatus = 'disconnected';
  private ipAddress: string = '';
  
  // Printer implementation
  print(document: Document): void {
    console.log(`🖨️ MFP: Printing "${document.name}" (${document.pages} pages)`);
    this.inkLevel -= document.pages * 0.5;
    this.printerStatus = 'printing';
    
    setTimeout(() => {
      this.printerStatus = 'ready';
      console.log('✅ Printing complete!');
    }, 2000);
  }
  
  getPrinterStatus(): PrinterStatus {
    return this.printerStatus;
  }
  
  getInkLevel(): number {
    return Math.max(0, this.inkLevel);
  }
  
  // Scanner implementation
  scan(settings: ScanSettings): ScannedDocument {
    console.log(`📄 MFP: Scanning at ${settings.resolution}dpi in ${settings.colorMode}`);
    this.scannerStatus = 'scanning';
    
    const scannedDoc: ScannedDocument = {
      id: `scan_${Date.now()}`,
      name: `Scan_${new Date().toISOString()}`,
      content: 'Scanned content...',
      pages: 1,
      resolution: settings.resolution,
      colorMode: settings.colorMode
    };
    
    setTimeout(() => {
      this.scannerStatus = 'ready';
      console.log('✅ Scanning complete!');
    }, 1500);
    
    return scannedDoc;
  }
  
  getScannerStatus(): ScannerStatus {
    return this.scannerStatus;
  }
  
  // Copier implementation
  copy(document: Document, copies: number): void {
    console.log(`📋 MFP: Making ${copies} copies of "${document.name}"`);
    this.copierStatus = 'copying';
    this.inkLevel -= document.pages * copies * 0.5;
    
    setTimeout(() => {
      this.copierStatus = 'ready';
      console.log(`✅ Created ${copies} copies!`);
    }, 1000 * copies);
  }
  
  getCopierStatus(): CopierStatus {
    return this.copierStatus;
  }
  
  // Network implementation
  connect(network: string, password: string): void {
    console.log(`🌐 MFP: Connecting to ${network}...`);
    this.networkStatus = 'connecting';
    
    setTimeout(() => {
      this.networkStatus = 'connected';
      this.ipAddress = `192.168.1.${Math.floor(Math.random() * 100) + 100}`;
      console.log(`✅ Connected! IP: ${this.ipAddress}`);
    }, 2000);
  }
  
  disconnect(): void {
    console.log('🌐 MFP: Disconnecting from network...');
    this.networkStatus = 'disconnected';
    this.ipAddress = '';
  }
  
  getNetworkStatus(): NetworkStatus {
    return this.networkStatus;
  }
  
  getIPAddress(): string {
    return this.ipAddress;
  }
}

// 🖨️ Enterprise printer with all features
class EnterprisePrinter extends MultifunctionPrinter implements Fax, Stapler, CloudEnabled {
  private faxHistory: FaxRecord[] = [];
  private staplerStatus: StaplerStatus = 'ready';
  private stapleCount: number = 5000;
  private cloudAccounts: Map<string, any> = new Map();
  
  // Fax implementation
  sendFax(number: string, document: Document): void {
    console.log(`📠 Sending fax to ${number}: "${document.name}"`);
    
    const record: FaxRecord = {
      id: `fax_${Date.now()}`,
      number,
      timestamp: new Date(),
      pages: document.pages,
      status: 'sent'
    };
    
    this.faxHistory.push(record);
    console.log('✅ Fax sent successfully!');
  }
  
  receiveFax(): Document | null {
    if (Math.random() > 0.7) {
      const fax: Document = {
        id: `fax_${Date.now()}`,
        name: `Received_Fax_${new Date().toISOString()}`,
        content: 'Fax content...',
        pages: Math.floor(Math.random() * 5) + 1
      };
      
      console.log(`📠 Received fax: "${fax.name}" (${fax.pages} pages)`);
      
      this.faxHistory.push({
        id: fax.id,
        number: 'Unknown',
        timestamp: new Date(),
        pages: fax.pages,
        status: 'received'
      });
      
      return fax;
    }
    
    return null;
  }
  
  getFaxHistory(): FaxRecord[] {
    return [...this.faxHistory];
  }
  
  // Stapler implementation
  staple(pages: number): void {
    if (this.stapleCount < pages) {
      this.staplerStatus = 'empty';
      throw new Error('Not enough staples!');
    }
    
    console.log(`📎 Stapling ${pages} pages...`);
    this.stapleCount -= pages;
    
    if (this.stapleCount < 100) {
      this.staplerStatus = 'low_staples';
    }
    
    console.log('✅ Pages stapled!');
  }
  
  getStaplerStatus(): StaplerStatus {
    return this.staplerStatus;
  }
  
  refillStaples(): void {
    console.log('📎 Refilling staples...');
    this.stapleCount = 5000;
    this.staplerStatus = 'ready';
    console.log('✅ Stapler refilled!');
  }
  
  // Cloud implementation
  uploadToCloud(document: Document, service: string): void {
    if (!this.cloudAccounts.has(service)) {
      throw new Error(`Not connected to ${service}`);
    }
    
    console.log(`☁️ Uploading "${document.name}" to ${service}...`);
    console.log('✅ Upload complete!');
  }
  
  downloadFromCloud(documentId: string, service: string): Document {
    if (!this.cloudAccounts.has(service)) {
      throw new Error(`Not connected to ${service}`);
    }
    
    console.log(`☁️ Downloading ${documentId} from ${service}...`);
    
    return {
      id: documentId,
      name: `Downloaded_${documentId}`,
      content: 'Downloaded content...',
      pages: Math.floor(Math.random() * 10) + 1
    };
  }
  
  linkCloudAccount(service: string, credentials: any): void {
    console.log(`☁️ Linking ${service} account...`);
    this.cloudAccounts.set(service, credentials);
    console.log(`✅ ${service} account linked!`);
  }
}

// 🎯 Usage examples showing flexibility
function printDocument(printer: Printer, doc: Document): void {
  if (printer.getInkLevel() < 10) {
    console.log('⚠️ Low ink warning!');
  }
  printer.print(doc);
}

function scanAndPrint(device: Printer & Scanner): void {
  const scanned = device.scan({
    resolution: 300,
    colorMode: 'color',
    format: 'pdf'
  });
  
  device.print(scanned);
}

function networkPrint(device: Printer & NetworkDevice, doc: Document): void {
  if (device.getNetworkStatus() !== 'connected') {
    console.log('🌐 Connecting to network first...');
    device.connect('Office_WiFi', 'password123');
  }
  
  console.log(`🌐 Printing via network (IP: ${device.getIPAddress()})`);
  device.print(doc);
}

// 🚀 Demo
const testDoc: Document = {
  id: 'doc1',
  name: 'Quarterly Report',
  content: 'Financial data...',
  pages: 10
};

console.log('=== Basic Printer ===');
const basicPrinter = new BasicPrinter();
printDocument(basicPrinter, testDoc);

console.log('\n=== Multifunction Printer ===');
const mfp = new MultifunctionPrinter();
mfp.connect('Office_WiFi', 'password123');
scanAndPrint(mfp);
networkPrint(mfp, testDoc);

console.log('\n=== Enterprise Printer ===');
const enterprise = new EnterprisePrinter();
enterprise.linkCloudAccount('GoogleDrive', { token: 'abc123' });
enterprise.uploadToCloud(testDoc, 'GoogleDrive');
enterprise.sendFax('+1234567890', testDoc);
enterprise.staple(5);

🏪 E-commerce System

Let’s apply ISP to an e-commerce platform:

// 🏪 E-commerce interfaces following ISP

// 🛍️ Product interfaces
interface Viewable {
  display(): ProductView;
  getImages(): string[];
  getDescription(): string;
}

interface Purchasable {
  price: number;
  currency: string;
  addToCart(quantity: number): CartItem;
  isInStock(): boolean;
  getStockLevel(): number;
}

interface Discountable {
  applyDiscount(code: string): number;
  getActiveDiscounts(): Discount[];
  calculateFinalPrice(): number;
}

interface Reviewable {
  getReviews(): Review[];
  addReview(review: Review): void;
  getAverageRating(): number;
}

interface Shippable {
  weight: number;
  dimensions: Dimensions;
  calculateShipping(destination: Address): ShippingCost;
  getShippingMethods(): ShippingMethod[];
}

interface Digital {
  downloadUrl: string;
  fileSize: number;
  generateDownloadLink(orderId: string): string;
  getDownloadExpiry(): Date;
}

interface Trackable {
  track(event: TrackingEvent): void;
  getAnalytics(): ProductAnalytics;
}

interface Wishlistable {
  addToWishlist(userId: string): void;
  removeFromWishlist(userId: string): void;
  getWishlistCount(): number;
}

// 🎯 Supporting types
interface ProductView {
  id: string;
  name: string;
  images: string[];
  description: string;
  price: number;
  currency: string;
}

interface CartItem {
  productId: string;
  productName: string;
  quantity: number;
  unitPrice: number;
  totalPrice: number;
}

interface Discount {
  code: string;
  percentage: number;
  validUntil: Date;
}

interface Review {
  id: string;
  userId: string;
  rating: number;
  comment: string;
  timestamp: Date;
}

interface Dimensions {
  length: number;
  width: number;
  height: number;
  unit: 'cm' | 'inch';
}

interface Address {
  street: string;
  city: string;
  state: string;
  zipCode: string;
  country: string;
}

interface ShippingCost {
  method: string;
  cost: number;
  estimatedDays: number;
}

interface ShippingMethod {
  name: string;
  carrier: string;
  estimatedDays: number;
  basePrice: number;
}

interface TrackingEvent {
  event: 'view' | 'add_to_cart' | 'purchase' | 'review';
  timestamp: Date;
  userId?: string;
  metadata?: any;
}

interface ProductAnalytics {
  views: number;
  purchases: number;
  cartAdds: number;
  conversionRate: number;
}

// 📦 Physical product implementation
class PhysicalProduct implements Viewable, Purchasable, Shippable, Reviewable, Wishlistable, Trackable {
  id: string;
  name: string;
  description: string;
  images: string[];
  price: number;
  currency: string = 'USD';
  weight: number;
  dimensions: Dimensions;
  stock: number;
  
  private reviews: Review[] = [];
  private wishlistUsers: Set<string> = new Set();
  private analytics: ProductAnalytics = {
    views: 0,
    purchases: 0,
    cartAdds: 0,
    conversionRate: 0
  };
  
  constructor(
    id: string,
    name: string,
    description: string,
    images: string[],
    price: number,
    weight: number,
    dimensions: Dimensions,
    stock: number
  ) {
    this.id = id;
    this.name = name;
    this.description = description;
    this.images = images;
    this.price = price;
    this.weight = weight;
    this.dimensions = dimensions;
    this.stock = stock;
  }
  
  // Viewable implementation
  display(): ProductView {
    this.track({ event: 'view', timestamp: new Date() });
    
    return {
      id: this.id,
      name: this.name,
      images: this.images,
      description: this.description,
      price: this.price,
      currency: this.currency
    };
  }
  
  getImages(): string[] {
    return [...this.images];
  }
  
  getDescription(): string {
    return this.description;
  }
  
  // Purchasable implementation
  addToCart(quantity: number): CartItem {
    if (quantity > this.stock) {
      throw new Error(`Only ${this.stock} items available`);
    }
    
    this.track({ 
      event: 'add_to_cart', 
      timestamp: new Date(),
      metadata: { quantity }
    });
    
    return {
      productId: this.id,
      productName: this.name,
      quantity,
      unitPrice: this.price,
      totalPrice: this.price * quantity
    };
  }
  
  isInStock(): boolean {
    return this.stock > 0;
  }
  
  getStockLevel(): number {
    return this.stock;
  }
  
  // Shippable implementation
  calculateShipping(destination: Address): ShippingCost {
    const baseRate = 5;
    const weightRate = this.weight * 0.5;
    const distanceMultiplier = destination.country === 'USA' ? 1 : 2.5;
    
    return {
      method: 'Standard',
      cost: (baseRate + weightRate) * distanceMultiplier,
      estimatedDays: destination.country === 'USA' ? 5 : 14
    };
  }
  
  getShippingMethods(): ShippingMethod[] {
    return [
      { name: 'Standard', carrier: 'USPS', estimatedDays: 5, basePrice: 5 },
      { name: 'Express', carrier: 'FedEx', estimatedDays: 2, basePrice: 15 },
      { name: 'Overnight', carrier: 'UPS', estimatedDays: 1, basePrice: 30 }
    ];
  }
  
  // Reviewable implementation
  getReviews(): Review[] {
    return [...this.reviews];
  }
  
  addReview(review: Review): void {
    this.reviews.push(review);
    this.track({ 
      event: 'review', 
      timestamp: new Date(),
      userId: review.userId,
      metadata: { rating: review.rating }
    });
  }
  
  getAverageRating(): number {
    if (this.reviews.length === 0) return 0;
    
    const sum = this.reviews.reduce((acc, review) => acc + review.rating, 0);
    return sum / this.reviews.length;
  }
  
  // Wishlistable implementation
  addToWishlist(userId: string): void {
    this.wishlistUsers.add(userId);
    console.log(`❤️ User ${userId} added ${this.name} to wishlist`);
  }
  
  removeFromWishlist(userId: string): void {
    this.wishlistUsers.delete(userId);
    console.log(`💔 User ${userId} removed ${this.name} from wishlist`);
  }
  
  getWishlistCount(): number {
    return this.wishlistUsers.size;
  }
  
  // Trackable implementation
  track(event: TrackingEvent): void {
    switch (event.event) {
      case 'view':
        this.analytics.views++;
        break;
      case 'add_to_cart':
        this.analytics.cartAdds++;
        break;
      case 'purchase':
        this.analytics.purchases++;
        break;
    }
    
    this.analytics.conversionRate = 
      this.analytics.views > 0 
        ? (this.analytics.purchases / this.analytics.views) * 100
        : 0;
  }
  
  getAnalytics(): ProductAnalytics {
    return { ...this.analytics };
  }
}

// 💾 Digital product implementation
class DigitalProduct implements Viewable, Purchasable, Digital, Reviewable, Trackable {
  id: string;
  name: string;
  description: string;
  images: string[];
  price: number;
  currency: string = 'USD';
  downloadUrl: string;
  fileSize: number;
  
  private reviews: Review[] = [];
  private analytics: ProductAnalytics = {
    views: 0,
    purchases: 0,
    cartAdds: 0,
    conversionRate: 0
  };
  
  constructor(
    id: string,
    name: string,
    description: string,
    images: string[],
    price: number,
    downloadUrl: string,
    fileSize: number
  ) {
    this.id = id;
    this.name = name;
    this.description = description;
    this.images = images;
    this.price = price;
    this.downloadUrl = downloadUrl;
    this.fileSize = fileSize;
  }
  
  // Viewable implementation
  display(): ProductView {
    this.track({ event: 'view', timestamp: new Date() });
    
    return {
      id: this.id,
      name: this.name,
      images: this.images,
      description: this.description,
      price: this.price,
      currency: this.currency
    };
  }
  
  getImages(): string[] {
    return [...this.images];
  }
  
  getDescription(): string {
    return this.description;
  }
  
  // Purchasable implementation
  addToCart(quantity: number): CartItem {
    this.track({ 
      event: 'add_to_cart', 
      timestamp: new Date(),
      metadata: { quantity }
    });
    
    return {
      productId: this.id,
      productName: this.name,
      quantity,
      unitPrice: this.price,
      totalPrice: this.price * quantity
    };
  }
  
  isInStock(): boolean {
    return true; // Digital products are always "in stock"
  }
  
  getStockLevel(): number {
    return Infinity; // Unlimited digital copies
  }
  
  // Digital implementation
  generateDownloadLink(orderId: string): string {
    const token = Buffer.from(`${orderId}:${this.id}`).toString('base64');
    return `${this.downloadUrl}?token=${token}`;
  }
  
  getDownloadExpiry(): Date {
    const expiry = new Date();
    expiry.setDate(expiry.getDate() + 30); // 30 days expiry
    return expiry;
  }
  
  // Reviewable implementation
  getReviews(): Review[] {
    return [...this.reviews];
  }
  
  addReview(review: Review): void {
    this.reviews.push(review);
    this.track({ 
      event: 'review', 
      timestamp: new Date(),
      userId: review.userId,
      metadata: { rating: review.rating }
    });
  }
  
  getAverageRating(): number {
    if (this.reviews.length === 0) return 0;
    
    const sum = this.reviews.reduce((acc, review) => acc + review.rating, 0);
    return sum / this.reviews.length;
  }
  
  // Trackable implementation
  track(event: TrackingEvent): void {
    switch (event.event) {
      case 'view':
        this.analytics.views++;
        break;
      case 'add_to_cart':
        this.analytics.cartAdds++;
        break;
      case 'purchase':
        this.analytics.purchases++;
        break;
    }
    
    this.analytics.conversionRate = 
      this.analytics.views > 0 
        ? (this.analytics.purchases / this.analytics.views) * 100
        : 0;
  }
  
  getAnalytics(): ProductAnalytics {
    return { ...this.analytics };
  }
}

// 🎫 Subscription product with discounts
class SubscriptionProduct extends DigitalProduct implements Discountable {
  private discounts: Map<string, Discount> = new Map();
  private billingPeriod: 'monthly' | 'yearly';
  
  constructor(
    id: string,
    name: string,
    description: string,
    images: string[],
    price: number,
    billingPeriod: 'monthly' | 'yearly'
  ) {
    super(
      id,
      name,
      description,
      images,
      price,
      'subscription_portal',
      0
    );
    this.billingPeriod = billingPeriod;
  }
  
  // Discountable implementation
  applyDiscount(code: string): number {
    const discount = this.discounts.get(code);
    
    if (!discount) {
      throw new Error('Invalid discount code');
    }
    
    if (discount.validUntil < new Date()) {
      throw new Error('Discount code expired');
    }
    
    return this.price * (1 - discount.percentage / 100);
  }
  
  getActiveDiscounts(): Discount[] {
    const now = new Date();
    return Array.from(this.discounts.values())
      .filter(d => d.validUntil > now);
  }
  
  calculateFinalPrice(): number {
    const activeDiscounts = this.getActiveDiscounts();
    
    if (activeDiscounts.length === 0) {
      return this.price;
    }
    
    // Apply the best discount
    const bestDiscount = activeDiscounts.reduce((best, current) => 
      current.percentage > best.percentage ? current : best
    );
    
    return this.price * (1 - bestDiscount.percentage / 100);
  }
  
  addDiscount(discount: Discount): void {
    this.discounts.set(discount.code, discount);
    console.log(`🎟️ Added discount ${discount.code} (${discount.percentage}% off)`);
  }
  
  getBillingPeriod(): string {
    return this.billingPeriod;
  }
}

// 🎯 Usage showing interface segregation benefits
function displayProduct(product: Viewable): void {
  const view = product.display();
  console.log(`📦 ${view.name}: ${view.description.substring(0, 50)}...`);
  console.log(`💰 Price: ${view.currency} ${view.price}`);
}

function shipProduct(product: Shippable, destination: Address): void {
  const shipping = product.calculateShipping(destination);
  console.log(`🚚 Shipping to ${destination.city}, ${destination.country}`);
  console.log(`💵 Cost: $${shipping.cost} (${shipping.estimatedDays} days)`);
}

function processDigitalPurchase(product: Digital & Purchasable, orderId: string): void {
  if (!product.isInStock()) {
    throw new Error('Product not available');
  }
  
  const downloadLink = product.generateDownloadLink(orderId);
  const expiry = product.getDownloadExpiry();
  
  console.log(`💾 Digital purchase processed!`);
  console.log(`🔗 Download: ${downloadLink}`);
  console.log(`⏰ Expires: ${expiry.toLocaleDateString()}`);
}

// 🚀 Demo
console.log('=== Physical Product ===');
const laptop = new PhysicalProduct(
  'laptop001',
  'Gaming Laptop Pro',
  'High-performance gaming laptop with RTX graphics',
  ['laptop1.jpg', 'laptop2.jpg'],
  1299.99,
  2.5,
  { length: 35, width: 25, height: 2, unit: 'cm' },
  10
);

displayProduct(laptop);
const cartItem = laptop.addToCart(1);
console.log(`🛒 Added to cart: ${cartItem.productName} x${cartItem.quantity}`);

shipProduct(laptop, {
  street: '123 Main St',
  city: 'New York',
  state: 'NY',
  zipCode: '10001',
  country: 'USA'
});

console.log('\n=== Digital Product ===');
const ebook = new DigitalProduct(
  'ebook001',
  'TypeScript Mastery',
  'Complete guide to mastering TypeScript',
  ['cover.jpg'],
  39.99,
  'https://downloads.example.com/ts-mastery',
  15000000
);

displayProduct(ebook);
processDigitalPurchase(ebook, 'order123');

console.log('\n=== Subscription Product ===');
const subscription = new SubscriptionProduct(
  'sub001',
  'Premium Membership',
  'Access to all premium content and features',
  ['premium.jpg'],
  19.99,
  'monthly'
);

subscription.addDiscount({
  code: 'SAVE20',
  percentage: 20,
  validUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
});

displayProduct(subscription);
console.log(`💳 Regular price: $${subscription.price}/${subscription.getBillingPeriod()}`);
console.log(`🎯 With discount: $${subscription.applyDiscount('SAVE20')}/${subscription.getBillingPeriod()}`);

⚠️ Common Pitfalls and Solutions

😱 Pitfall 1: Over-Segregation

// ❌ Wrong - too many tiny interfaces
interface HasId { id: string; }
interface HasName { name: string; }
interface HasEmail { email: string; }
interface HasPhone { phone: string; }
// ... 20 more single-property interfaces

class User implements HasId, HasName, HasEmail, HasPhone /* ... */ {
  // This becomes unmanageable!
}

// ✅ Better - group related properties
interface Identifiable {
  id: string;
  uuid?: string;
}

interface ContactInfo {
  email: string;
  phone?: string;
  alternateEmail?: string;
}

interface PersonalInfo {
  name: string;
  dateOfBirth?: Date;
  gender?: string;
}

class User implements Identifiable, ContactInfo, PersonalInfo {
  // Much cleaner and logical grouping
}

🤯 Pitfall 2: Wrong Abstraction Level

// ❌ Wrong - mixing abstraction levels
interface Vehicle {
  startEngine(): void;
  stopEngine(): void;
  openTrunk(): void; // Not all vehicles have trunks!
  foldWings(): void; // Not all vehicles have wings!
}

// ✅ Better - proper abstraction levels
interface Vehicle {
  start(): void;
  stop(): void;
}

interface Car extends Vehicle {
  openTrunk(): void;
  honk(): void;
}

interface Airplane extends Vehicle {
  foldWings(): void;
  engageAutopilot(): void;
}

interface Boat extends Vehicle {
  dropAnchor(): void;
  soundHorn(): void;
}

🛠️ Best Practices

🎯 ISP Guidelines

  1. Single Purpose 🎯: Each interface should have one clear purpose
  2. Client-Focused 👥: Design interfaces based on client needs
  3. Prefer Many to Few 🔢: Multiple small interfaces > one large interface
  4. Cohesion 🤝: Group related methods together
// 🌟 Example of well-designed interfaces
interface DataReader<T> {
  read(id: string): Promise<T>;
  readAll(): Promise<T[]>;
  exists(id: string): Promise<boolean>;
}

interface DataWriter<T> {
  create(data: Omit<T, 'id'>): Promise<T>;
  update(id: string, data: Partial<T>): Promise<T>;
  delete(id: string): Promise<void>;
}

interface DataSearcher<T> {
  search(query: string): Promise<T[]>;
  filter(predicate: (item: T) => boolean): Promise<T[]>;
}

interface DataAggregator<T> {
  count(): Promise<number>;
  groupBy<K extends keyof T>(field: K): Promise<Map<T[K], T[]>>;
  aggregate<R>(operation: (items: T[]) => R): Promise<R>;
}

// Classes can implement only what they need
class ReadOnlyRepository<T> implements DataReader<T>, DataSearcher<T> {
  // Implementation for read and search only
}

class FullRepository<T> implements DataReader<T>, DataWriter<T>, DataSearcher<T>, DataAggregator<T> {
  // Full CRUD + search + aggregation
}

class CacheRepository<T> implements DataReader<T>, DataWriter<T> {
  // Simple cache with read/write
}

🧪 Hands-On Exercise

🎯 Challenge: Design a Media Player System

Create a media player system that follows ISP:

📋 Requirements:

  • ✅ Support different media types (audio, video, streaming)
  • 🎨 Different playback capabilities
  • 🎯 Various quality settings
  • 📊 Analytics and tracking
  • 🔧 Plugin system for extensions

🚀 Bonus Points:

  • Add playlist management
  • Implement subtitle support
  • Create equalizer interface

💡 Solution

🔍 Click to see solution
// 🎯 Media player interfaces following ISP

// 🎵 Core playback interfaces
interface Playable {
  play(): void;
  pause(): void;
  stop(): void;
  isPlaying(): boolean;
}

interface Seekable {
  seek(position: number): void;
  getCurrentPosition(): number;
  getDuration(): number;
}

interface VolumeControl {
  setVolume(level: number): void;
  getVolume(): number;
  mute(): void;
  unmute(): void;
  isMuted(): boolean;
}

interface SpeedControl {
  setPlaybackSpeed(speed: number): void;
  getPlaybackSpeed(): number;
  getSupportedSpeeds(): number[];
}

// 🎥 Video-specific interfaces
interface VideoPlayback {
  setVideoQuality(quality: VideoQuality): void;
  getVideoQuality(): VideoQuality;
  getAvailableQualities(): VideoQuality[];
  enterFullscreen(): void;
  exitFullscreen(): void;
  isFullscreen(): boolean;
}

interface SubtitleSupport {
  loadSubtitles(url: string): void;
  enableSubtitles(): void;
  disableSubtitles(): void;
  setSubtitleTrack(trackId: number): void;
  getSubtitleTracks(): SubtitleTrack[];
}

interface PictureInPicture {
  enterPiP(): void;
  exitPiP(): void;
  isPiPSupported(): boolean;
  isPiPActive(): boolean;
}

// 🎵 Audio-specific interfaces
interface AudioEnhancement {
  setEqualizer(preset: EqualizerPreset): void;
  getEqualizer(): EqualizerPreset;
  getEqualizerPresets(): EqualizerPreset[];
  setBassBoost(level: number): void;
  setTrebleBoost(level: number): void;
}

interface AudioVisualization {
  enableVisualizer(type: VisualizerType): void;
  disableVisualizer(): void;
  getVisualizerTypes(): VisualizerType[];
  getFrequencyData(): Uint8Array;
}

// 📡 Streaming interfaces
interface StreamingCapable {
  setStreamQuality(quality: StreamQuality): void;
  getStreamQuality(): StreamQuality;
  getBufferLevel(): number;
  preload(): void;
}

interface LiveStreamSupport {
  isLive(): boolean;
  goToLive(): void;
  getLatency(): number;
  setLowLatencyMode(enabled: boolean): void;
}

interface AdaptiveBitrate {
  enableABR(): void;
  disableABR(): void;
  isABREnabled(): boolean;
  getCurrentBitrate(): number;
  getAvailableBitrates(): number[];
}

// 📋 Playlist interfaces
interface PlaylistManagement {
  loadPlaylist(playlist: Playlist): void;
  next(): void;
  previous(): void;
  shuffle(): void;
  setRepeatMode(mode: RepeatMode): void;
  getCurrentTrack(): Track;
  getPlaylist(): Playlist;
}

interface QueueManagement {
  addToQueue(track: Track): void;
  removeFromQueue(trackId: string): void;
  clearQueue(): void;
  getQueue(): Track[];
  moveInQueue(trackId: string, newPosition: number): void;
}

// 📊 Analytics interfaces
interface PlaybackAnalytics {
  trackEvent(event: AnalyticsEvent): void;
  getPlaybackStats(): PlaybackStats;
  getSessionDuration(): number;
}

interface QualityAnalytics {
  getQualityMetrics(): QualityMetrics;
  getBufferingEvents(): BufferingEvent[];
  getErrorRate(): number;
}

// 🔌 Plugin interfaces
interface PluginHost {
  loadPlugin(plugin: MediaPlugin): void;
  unloadPlugin(pluginId: string): void;
  getLoadedPlugins(): MediaPlugin[];
  enablePlugin(pluginId: string): void;
  disablePlugin(pluginId: string): void;
}

interface MediaPlugin {
  id: string;
  name: string;
  version: string;
  initialize(player: any): void;
  destroy(): void;
}

// 📝 Supporting types
type VideoQuality = '144p' | '240p' | '360p' | '480p' | '720p' | '1080p' | '1440p' | '4K' | '8K';
type StreamQuality = 'auto' | 'low' | 'medium' | 'high' | 'ultra';
type RepeatMode = 'none' | 'one' | 'all';
type VisualizerType = 'bars' | 'wave' | 'circular' | 'spectrum';

interface SubtitleTrack {
  id: number;
  language: string;
  label: string;
  kind: 'subtitles' | 'captions' | 'descriptions';
}

interface EqualizerPreset {
  name: string;
  frequencies: number[];
  gains: number[];
}

interface Track {
  id: string;
  title: string;
  artist?: string;
  album?: string;
  duration: number;
  url: string;
  thumbnail?: string;
}

interface Playlist {
  id: string;
  name: string;
  tracks: Track[];
  createdAt: Date;
}

interface AnalyticsEvent {
  type: 'play' | 'pause' | 'seek' | 'quality_change' | 'error';
  timestamp: Date;
  metadata?: any;
}

interface PlaybackStats {
  totalPlayTime: number;
  averageSessionLength: number;
  completionRate: number;
  mostPlayedContent: string[];
}

interface QualityMetrics {
  averageBitrate: number;
  droppedFrames: number;
  bufferingRatio: number;
  startupTime: number;
}

interface BufferingEvent {
  timestamp: Date;
  duration: number;
  reason?: string;
}

// 🎵 Audio player implementation
class AudioPlayer implements Playable, Seekable, VolumeControl, SpeedControl, PlaylistManagement, AudioEnhancement {
  private isPlayingState = false;
  private currentPosition = 0;
  private duration = 0;
  private volume = 100;
  private muted = false;
  private playbackSpeed = 1;
  private playlist: Playlist | null = null;
  private currentTrackIndex = 0;
  private repeatMode: RepeatMode = 'none';
  private equalizer: EqualizerPreset = {
    name: 'Flat',
    frequencies: [60, 230, 910, 3600, 14000],
    gains: [0, 0, 0, 0, 0]
  };
  
  // Playable implementation
  play(): void {
    console.log('▶️ Playing audio...');
    this.isPlayingState = true;
  }
  
  pause(): void {
    console.log('⏸️ Pausing audio...');
    this.isPlayingState = false;
  }
  
  stop(): void {
    console.log('⏹️ Stopping audio...');
    this.isPlayingState = false;
    this.currentPosition = 0;
  }
  
  isPlaying(): boolean {
    return this.isPlayingState;
  }
  
  // Seekable implementation
  seek(position: number): void {
    console.log(`⏩ Seeking to ${position}s`);
    this.currentPosition = Math.max(0, Math.min(position, this.duration));
  }
  
  getCurrentPosition(): number {
    return this.currentPosition;
  }
  
  getDuration(): number {
    return this.duration;
  }
  
  // VolumeControl implementation
  setVolume(level: number): void {
    this.volume = Math.max(0, Math.min(100, level));
    console.log(`🔊 Volume: ${this.volume}%`);
  }
  
  getVolume(): number {
    return this.volume;
  }
  
  mute(): void {
    this.muted = true;
    console.log('🔇 Muted');
  }
  
  unmute(): void {
    this.muted = false;
    console.log('🔊 Unmuted');
  }
  
  isMuted(): boolean {
    return this.muted;
  }
  
  // SpeedControl implementation
  setPlaybackSpeed(speed: number): void {
    this.playbackSpeed = speed;
    console.log(`⚡ Playback speed: ${speed}x`);
  }
  
  getPlaybackSpeed(): number {
    return this.playbackSpeed;
  }
  
  getSupportedSpeeds(): number[] {
    return [0.5, 0.75, 1, 1.25, 1.5, 2];
  }
  
  // PlaylistManagement implementation
  loadPlaylist(playlist: Playlist): void {
    this.playlist = playlist;
    this.currentTrackIndex = 0;
    console.log(`📋 Loaded playlist: ${playlist.name} (${playlist.tracks.length} tracks)`);
  }
  
  next(): void {
    if (!this.playlist) return;
    
    this.currentTrackIndex = (this.currentTrackIndex + 1) % this.playlist.tracks.length;
    console.log(`⏭️ Next track: ${this.getCurrentTrack().title}`);
  }
  
  previous(): void {
    if (!this.playlist) return;
    
    this.currentTrackIndex = this.currentTrackIndex === 0 
      ? this.playlist.tracks.length - 1 
      : this.currentTrackIndex - 1;
    console.log(`⏮️ Previous track: ${this.getCurrentTrack().title}`);
  }
  
  shuffle(): void {
    if (!this.playlist) return;
    
    // Fisher-Yates shuffle
    const tracks = [...this.playlist.tracks];
    for (let i = tracks.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [tracks[i], tracks[j]] = [tracks[j], tracks[i]];
    }
    
    this.playlist.tracks = tracks;
    console.log('🔀 Playlist shuffled');
  }
  
  setRepeatMode(mode: RepeatMode): void {
    this.repeatMode = mode;
    console.log(`🔁 Repeat mode: ${mode}`);
  }
  
  getCurrentTrack(): Track {
    if (!this.playlist || this.playlist.tracks.length === 0) {
      throw new Error('No track loaded');
    }
    
    return this.playlist.tracks[this.currentTrackIndex];
  }
  
  getPlaylist(): Playlist {
    if (!this.playlist) {
      throw new Error('No playlist loaded');
    }
    
    return this.playlist;
  }
  
  // AudioEnhancement implementation
  setEqualizer(preset: EqualizerPreset): void {
    this.equalizer = preset;
    console.log(`🎚️ Equalizer: ${preset.name}`);
  }
  
  getEqualizer(): EqualizerPreset {
    return this.equalizer;
  }
  
  getEqualizerPresets(): EqualizerPreset[] {
    return [
      { name: 'Flat', frequencies: [60, 230, 910, 3600, 14000], gains: [0, 0, 0, 0, 0] },
      { name: 'Rock', frequencies: [60, 230, 910, 3600, 14000], gains: [5, 3, 0, 2, 4] },
      { name: 'Jazz', frequencies: [60, 230, 910, 3600, 14000], gains: [3, 2, 0, 2, 3] },
      { name: 'Classical', frequencies: [60, 230, 910, 3600, 14000], gains: [0, 0, 0, 2, 3] }
    ];
  }
  
  setBassBoost(level: number): void {
    console.log(`🔊 Bass boost: ${level}`);
  }
  
  setTrebleBoost(level: number): void {
    console.log(`🔊 Treble boost: ${level}`);
  }
}

// 🎥 Video player implementation
class VideoPlayer extends AudioPlayer implements VideoPlayback, SubtitleSupport, PictureInPicture {
  private videoQuality: VideoQuality = '720p';
  private fullscreen = false;
  private subtitlesEnabled = false;
  private subtitleTracks: SubtitleTrack[] = [];
  private currentSubtitleTrack = 0;
  private pipActive = false;
  
  // VideoPlayback implementation
  setVideoQuality(quality: VideoQuality): void {
    this.videoQuality = quality;
    console.log(`📺 Video quality: ${quality}`);
  }
  
  getVideoQuality(): VideoQuality {
    return this.videoQuality;
  }
  
  getAvailableQualities(): VideoQuality[] {
    return ['360p', '480p', '720p', '1080p'];
  }
  
  enterFullscreen(): void {
    this.fullscreen = true;
    console.log('📺 Entered fullscreen');
  }
  
  exitFullscreen(): void {
    this.fullscreen = false;
    console.log('📺 Exited fullscreen');
  }
  
  isFullscreen(): boolean {
    return this.fullscreen;
  }
  
  // SubtitleSupport implementation
  loadSubtitles(url: string): void {
    console.log(`📝 Loading subtitles from: ${url}`);
    
    this.subtitleTracks.push({
      id: this.subtitleTracks.length,
      language: 'en',
      label: 'English',
      kind: 'subtitles'
    });
  }
  
  enableSubtitles(): void {
    this.subtitlesEnabled = true;
    console.log('📝 Subtitles enabled');
  }
  
  disableSubtitles(): void {
    this.subtitlesEnabled = false;
    console.log('📝 Subtitles disabled');
  }
  
  setSubtitleTrack(trackId: number): void {
    this.currentSubtitleTrack = trackId;
    console.log(`📝 Subtitle track: ${trackId}`);
  }
  
  getSubtitleTracks(): SubtitleTrack[] {
    return [...this.subtitleTracks];
  }
  
  // PictureInPicture implementation
  enterPiP(): void {
    if (!this.isPiPSupported()) {
      throw new Error('PiP not supported');
    }
    
    this.pipActive = true;
    console.log('🖼️ Entered Picture-in-Picture');
  }
  
  exitPiP(): void {
    this.pipActive = false;
    console.log('🖼️ Exited Picture-in-Picture');
  }
  
  isPiPSupported(): boolean {
    return true; // Simplified
  }
  
  isPiPActive(): boolean {
    return this.pipActive;
  }
}

// 📡 Streaming player implementation
class StreamingPlayer extends VideoPlayer implements StreamingCapable, LiveStreamSupport, AdaptiveBitrate, PlaybackAnalytics {
  private streamQuality: StreamQuality = 'auto';
  private bufferLevel = 0;
  private isLiveStream = false;
  private abrEnabled = true;
  private currentBitrate = 5000;
  private analytics: AnalyticsEvent[] = [];
  private sessionStart = new Date();
  
  // StreamingCapable implementation
  setStreamQuality(quality: StreamQuality): void {
    this.streamQuality = quality;
    console.log(`📡 Stream quality: ${quality}`);
    
    this.trackEvent({
      type: 'quality_change',
      timestamp: new Date(),
      metadata: { quality }
    });
  }
  
  getStreamQuality(): StreamQuality {
    return this.streamQuality;
  }
  
  getBufferLevel(): number {
    return this.bufferLevel;
  }
  
  preload(): void {
    console.log('⏳ Preloading stream...');
    this.bufferLevel = 30; // 30 seconds buffered
  }
  
  // LiveStreamSupport implementation
  isLive(): boolean {
    return this.isLiveStream;
  }
  
  goToLive(): void {
    if (!this.isLiveStream) {
      throw new Error('Not a live stream');
    }
    
    console.log('🔴 Going to live edge');
    this.seek(this.getDuration());
  }
  
  getLatency(): number {
    return this.isLiveStream ? 5.2 : 0; // 5.2 seconds latency
  }
  
  setLowLatencyMode(enabled: boolean): void {
    console.log(`⚡ Low latency mode: ${enabled ? 'ON' : 'OFF'}`);
  }
  
  // AdaptiveBitrate implementation
  enableABR(): void {
    this.abrEnabled = true;
    console.log('📊 Adaptive bitrate enabled');
  }
  
  disableABR(): void {
    this.abrEnabled = false;
    console.log('📊 Adaptive bitrate disabled');
  }
  
  isABREnabled(): boolean {
    return this.abrEnabled;
  }
  
  getCurrentBitrate(): number {
    return this.currentBitrate;
  }
  
  getAvailableBitrates(): number[] {
    return [500, 1000, 2500, 5000, 8000, 12000]; // kbps
  }
  
  // PlaybackAnalytics implementation
  trackEvent(event: AnalyticsEvent): void {
    this.analytics.push(event);
    console.log(`📊 Analytics: ${event.type} at ${event.timestamp.toISOString()}`);
  }
  
  getPlaybackStats(): PlaybackStats {
    const playEvents = this.analytics.filter(e => e.type === 'play').length;
    const totalTime = (new Date().getTime() - this.sessionStart.getTime()) / 1000;
    
    return {
      totalPlayTime: totalTime,
      averageSessionLength: totalTime / Math.max(playEvents, 1),
      completionRate: 0.85, // Simplified
      mostPlayedContent: ['Video 1', 'Video 2']
    };
  }
  
  getSessionDuration(): number {
    return (new Date().getTime() - this.sessionStart.getTime()) / 1000;
  }
  
  // Override play to track analytics
  play(): void {
    super.play();
    this.trackEvent({ type: 'play', timestamp: new Date() });
  }
  
  pause(): void {
    super.pause();
    this.trackEvent({ type: 'pause', timestamp: new Date() });
  }
  
  seek(position: number): void {
    super.seek(position);
    this.trackEvent({ 
      type: 'seek', 
      timestamp: new Date(),
      metadata: { position }
    });
  }
}

// 🔌 Plugin system
class PluginManager implements PluginHost {
  private plugins = new Map<string, MediaPlugin>();
  private enabledPlugins = new Set<string>();
  
  loadPlugin(plugin: MediaPlugin): void {
    this.plugins.set(plugin.id, plugin);
    console.log(`🔌 Loaded plugin: ${plugin.name} v${plugin.version}`);
  }
  
  unloadPlugin(pluginId: string): void {
    const plugin = this.plugins.get(pluginId);
    if (plugin) {
      plugin.destroy();
      this.plugins.delete(pluginId);
      this.enabledPlugins.delete(pluginId);
      console.log(`🔌 Unloaded plugin: ${pluginId}`);
    }
  }
  
  getLoadedPlugins(): MediaPlugin[] {
    return Array.from(this.plugins.values());
  }
  
  enablePlugin(pluginId: string): void {
    if (this.plugins.has(pluginId)) {
      this.enabledPlugins.add(pluginId);
      console.log(`✅ Enabled plugin: ${pluginId}`);
    }
  }
  
  disablePlugin(pluginId: string): void {
    this.enabledPlugins.delete(pluginId);
    console.log(`❌ Disabled plugin: ${pluginId}`);
  }
}

// 🎯 Usage examples showing ISP benefits
function basicPlayback(player: Playable & VolumeControl): void {
  player.setVolume(80);
  player.play();
  
  setTimeout(() => {
    player.pause();
  }, 5000);
}

function enhancedAudio(player: Playable & AudioEnhancement): void {
  const presets = player.getEqualizerPresets();
  player.setEqualizer(presets.find(p => p.name === 'Rock')!);
  player.setBassBoost(3);
  player.play();
}

function videoStreaming(player: VideoPlayback & StreamingCapable & AdaptiveBitrate): void {
  player.enableABR();
  player.setStreamQuality('auto');
  player.preload();
  
  const qualities = player.getAvailableQualities();
  console.log(`📺 Available qualities: ${qualities.join(', ')}`);
  
  if (player.getBufferLevel() > 10) {
    player.setVideoQuality('1080p');
  }
}

function liveStreamViewing(player: LiveStreamSupport & Playable): void {
  if (player.isLive()) {
    player.setLowLatencyMode(true);
    player.goToLive();
    console.log(`📡 Live stream latency: ${player.getLatency()}s`);
  }
  
  player.play();
}

// 🚀 Demo
console.log('=== Audio Player Demo ===');
const audioPlayer = new AudioPlayer();
const audioPlaylist: Playlist = {
  id: 'playlist1',
  name: 'My Favorites',
  tracks: [
    { id: '1', title: 'Song 1', artist: 'Artist A', duration: 180, url: 'song1.mp3' },
    { id: '2', title: 'Song 2', artist: 'Artist B', duration: 210, url: 'song2.mp3' },
    { id: '3', title: 'Song 3', artist: 'Artist C', duration: 195, url: 'song3.mp3' }
  ],
  createdAt: new Date()
};

audioPlayer.loadPlaylist(audioPlaylist);
basicPlayback(audioPlayer);
enhancedAudio(audioPlayer);

console.log('\n=== Video Player Demo ===');
const videoPlayer = new VideoPlayer();
videoPlayer.setVideoQuality('1080p');
videoPlayer.loadSubtitles('subtitles.vtt');
videoPlayer.enableSubtitles();

if (videoPlayer.isPiPSupported()) {
  videoPlayer.enterPiP();
}

console.log('\n=== Streaming Player Demo ===');
const streamingPlayer = new StreamingPlayer();
videoStreaming(streamingPlayer);

const stats = streamingPlayer.getPlaybackStats();
console.log(`📊 Total play time: ${stats.totalPlayTime}s`);
console.log(`📊 Session duration: ${streamingPlayer.getSessionDuration()}s`);

🎓 Key Takeaways

You now understand how to apply the Interface Segregation Principle! Here’s what you’ve learned:

  • Fat interfaces violate ISP and cause problems 🎯
  • Segregated interfaces provide flexibility and maintainability 🔀
  • Client-focused design creates better contracts 👥
  • Composition enables precise implementations 🧩
  • ISP is essential for professional code 🌟

Remember: No client should be forced to depend on methods it doesn’t use. Keep interfaces focused and cohesive! 🏗️

🤝 Next Steps

Congratulations! 🎉 You’ve mastered the Interface Segregation Principle!

Here’s what to do next:

  1. 💻 Practice with the media player exercise above
  2. 🏗️ Refactor existing fat interfaces in your codebase
  3. 📚 Move on to our next tutorial: Optional Properties in Interfaces: Flexible Contracts
  4. 🌟 Apply ISP to your real-world projects!

Remember: Great interfaces are like great tools - specialized, focused, and perfect for their job. Keep segregating! 🚀


Happy coding! 🎉🚀✨