# Design Patterns — Factory, Observer, Strategy, and Friends

<!-- hint:slides topic="Six essential design patterns: Singleton, Factory, Observer, Strategy, Decorator, Adapter — when and why to use each" slides="7" -->

Six essential design patterns with practical TypeScript examples and Mermaid class diagrams.

## 1. Singleton

**Problem:** Ensure a class has exactly one instance globally.

**Solution:** Private constructor, static `getInstance()` that returns the shared instance.

```typescript
class Database {
  private static instance: Database;

  private constructor() {}

  static getInstance(): Database {
    if (!Database.instance) {
      Database.instance = new Database();
    }
    return Database.instance;
  }

  query(sql: string) { /* ... */ }
}
```

```mermaid
classDiagram
    class Database {
        -static instance: Database
        -constructor()
        +static getInstance() Database
        +query(sql: string)
    }
```

**Tradeoff:** Simplifies access but creates global state; can complicate testing.

---

## 2. Factory

**Problem:** Object creation depends on configuration or runtime type; callers shouldn't know creation details.

**Solution:** Centralize creation in a factory function or class.

```typescript
type NotificationType = 'email' | 'sms' | 'push';

interface Notification {
  send(message: string): void;
}

class EmailNotification implements Notification {
  send(message: string) { /* ... */ }
}

class SmsNotification implements Notification {
  send(message: string) { /* ... */ }
}

function createNotification(type: NotificationType): Notification {
  switch (type) {
    case 'email': return new EmailNotification();
    case 'sms': return new SmsNotification();
    default: throw new Error(`Unknown type: ${type}`);
  }
}
```

```mermaid
classDiagram
    class Notification {
        <<interface>>
        +send(message: string)
    }
    class EmailNotification {
        +send(message: string)
    }
    class SmsNotification {
        +send(message: string)
    }
    class createNotification {
        +createNotification(type) Notification
    }
    Notification <|.. EmailNotification
    Notification <|.. SmsNotification
    createNotification ..> Notification : creates
```

---

## 3. Observer

**Problem:** Multiple objects need to react when another object changes. Loose coupling is required.

**Solution:** Subject holds a list of subscribers; when state changes, it notifies all of them.

```typescript
type Listener<T> = (data: T) => void;

class Subject<T> {
  private listeners: Set<Listener<T>> = new Set();

  subscribe(fn: Listener<T>): () => void {
    this.listeners.add(fn);
    return () => this.listeners.delete(fn);
  }

  notify(data: T): void {
    this.listeners.forEach(fn => fn(data));
  }
}

// Usage: form.notify({ email, submitted: true })
```

```mermaid
classDiagram
    class Subject {
        -listeners: Set
        +subscribe(fn)
        +notify(data)
    }
    class Observer {
        <<interface>>
        +update(data)
    }
    Subject --> Observer : notifies
```

---

## 4. Strategy

**Problem:** Multiple algorithms for the same task; want to switch at runtime without changing the client.

**Solution:** Extract each algorithm into a strategy object; client holds a strategy and delegates.

```mermaid
classDiagram
    class Context {
        -strategy: Strategy
        +setStrategy(s: Strategy)
        +execute()
    }
    class Strategy {
        <<interface>>
        +execute()
    }
    class ConcreteStrategyA {
        +execute()
    }
    class ConcreteStrategyB {
        +execute()
    }
    Context --> Strategy : uses
    Strategy <|.. ConcreteStrategyA
    Strategy <|.. ConcreteStrategyB
```

```typescript
interface PaymentStrategy {
  pay(amount: number): boolean;
}

class CreditCardStrategy implements PaymentStrategy {
  pay(amount: number) { /* ... */ return true; }
}

class PayPalStrategy implements PaymentStrategy {
  pay(amount: number) { /* ... */ return true; }
}

class PaymentProcessor {
  constructor(private strategy: PaymentStrategy) {}

  setStrategy(s: PaymentStrategy) { this.strategy = s; }

  execute(amount: number) {
    return this.strategy.pay(amount);
  }
}
```

```mermaid
classDiagram
    class PaymentStrategy {
        <<interface>>
        +pay(amount: number)
    }
    class CreditCardStrategy {
        +pay(amount: number)
    }
    class PayPalStrategy {
        +pay(amount: number)
    }
    class PaymentProcessor {
        -strategy: PaymentStrategy
        +setStrategy(s)
        +execute(amount)
    }
    PaymentStrategy <|.. CreditCardStrategy
    PaymentStrategy <|.. PayPalStrategy
    PaymentProcessor --> PaymentStrategy : delegates
```

---

## 5. Decorator

**Problem:** Add behavior to an object without subclassing; behavior should be stackable.

**Solution:** Wrap the object in a decorator that adds behavior and delegates to the wrapped object.

```typescript
interface Logger {
  log(msg: string): void;
}

class ConsoleLogger implements Logger {
  log(msg: string) { console.log(msg); }
}

class TimestampDecorator implements Logger {
  constructor(private inner: Logger) {}

  log(msg: string) {
    this.inner.log(`[${new Date().toISOString()}] ${msg}`);
  }
}

class LevelDecorator implements Logger {
  constructor(private inner: Logger, private level: string) {}

  log(msg: string) {
    this.inner.log(`[${this.level}] ${msg}`);
  }
}

// Usage: new TimestampDecorator(new LevelDecorator(new ConsoleLogger(), 'INFO'))
```

```mermaid
classDiagram
    class Logger {
        <<interface>>
        +log(msg: string)
    }
    class ConsoleLogger {
        +log(msg: string)
    }
    class TimestampDecorator {
        -inner: Logger
        +log(msg: string)
    }
    class LevelDecorator {
        -inner: Logger
        -level: string
        +log(msg: string)
    }
    Logger <|.. ConsoleLogger
    Logger <|.. TimestampDecorator
    Logger <|.. LevelDecorator
    TimestampDecorator --> Logger : wraps
    LevelDecorator --> Logger : wraps
```

---

## 6. Adapter

**Problem:** An existing class has the wrong interface for your needs (e.g., callback-based API, third-party library).

**Solution:** Create an adapter that implements your target interface and delegates to the adaptee.

```typescript
function fetchDataLegacy(callback: (err: Error | null, data?: string) => void): void {
  setTimeout(() => callback(null, 'result'), 100);
}

function adaptToPromise<T>(
  fn: (cb: (err: Error | null, data?: T) => void) => void
): Promise<T> {
  return new Promise((resolve, reject) => {
    fn((err, data) => err ? reject(err) : resolve(data as T));
  });
}

// Usage: const data = await adaptToPromise(fetchDataLegacy);
```

```mermaid
classDiagram
    class Target {
        <<interface>>
        +getData() Promise
    }
    class Adapter {
        -adaptee: Adaptee
        +getData() Promise
    }
    class Adaptee {
        +fetch(callback)
    }
    Target <|.. Adapter
    Adapter --> Adaptee : delegates
```

---

## Decision Flow

| Need | Pattern |
|------|---------|
| Same instance everywhere | Singleton |
| Create objects by type/config | Factory |
| Notify listeners on change | Observer |
| Swap algorithm at runtime | Strategy |
| Add behavior without subclassing | Decorator |
| Use legacy or wrong API | Adapter |

---

## Key Takeaways

1. **Singleton** — One instance; watch out for global state and testing.
2. **Factory** — Centralize creation; open for new types, closed for modification.
3. **Observer** — Loose coupling; subject notifies subscribers.
4. **Strategy** — Pluggable algorithms; client delegates to strategy.
5. **Decorator** — Wrap and delegate; stack decorators for layered behavior.
6. **Adapter** — Translate interfaces; no behavior change, just interface.
