Day 2 of 5

Structural Patterns

Structural patterns organize relationships between objects. Adapter wraps incompatible interfaces, Decorator adds behavior, and Facade simplifies complex subsystems.

typescript
// Adapter — make incompatible interfaces work together
// Old payment interface
class OldPayPal {
  makePayment(amount: number, currency: string) {
    console.log(\`PayPal: \${amount} \${currency}\`);
  }
}

// New interface your app expects
interface PaymentProcessor {
  charge(cents: number): void;
}

// Adapter bridges the gap
class PayPalAdapter implements PaymentProcessor {
  constructor(private paypal: OldPayPal) {}

  charge(cents: number) {
    this.paypal.makePayment(cents / 100, 'USD');
  }
}

const processor: PaymentProcessor = new PayPalAdapter(new OldPayPal());
processor.charge(4999); // PayPal: 49.99 USD
typescript
// Decorator — add behavior without subclassing
interface Logger {
  log(message: string): void;
}

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

class TimestampLogger implements Logger {
  constructor(private wrapped: Logger) {}
  log(message: string) {
    this.wrapped.log(\`[\${new Date().toISOString()}] \${message}\`);
  }
}

class PrefixLogger implements Logger {
  constructor(private wrapped: Logger, private prefix: string) {}
  log(message: string) { this.wrapped.log(\`\${this.prefix} \${message}\`); }
}

// Stack decorators
const logger = new PrefixLogger(
  new TimestampLogger(new ConsoleLogger()), '[INFO]');
logger.log('Server started');
// [INFO] [2026-04-10T...] Server started
typescript
// Facade — simplify a complex subsystem
class OrderFacade {
  constructor(
    private inventory: InventoryService,
    private payment: PaymentService,
    private shipping: ShippingService,
    private email: EmailService,
  ) {}

  async placeOrder(cart: Cart, card: CreditCard, address: Address) {
    await this.inventory.reserve(cart.items);
    await this.payment.charge(card, cart.total);
    const tracking = await this.shipping.dispatch(cart.items, address);
    await this.email.sendConfirmation(cart.userId, tracking);
    return tracking;
  }
}

Exercise: Refactor a Module Using These Patterns

  1. Find a function with 3+ different concerns
  2. Extract each concern into its own class
  3. Add an Adapter to wrap a third-party library
  4. Use a Facade to expose a simple API to callers
  5. Write tests against the Facade, not internals

Day 2 Summary

Finished this lesson?