iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Exercises

Three small refactors that reach for the right pattern.

Refactor exercises

EXAMPLE
// 1. Strategy: replace a switch with composable strategies

// Before
function discount(kind: string, total: number): number {
  switch (kind) {
    case 'student': return total * 0.85;
    case 'vip':     return total * 0.70;
    case 'none':    return total;
    default: throw new Error('unknown');
  }
}

// After
interface DiscountStrategy { apply(total: number): number; }
const strategies: Record<string, DiscountStrategy> = {
  student: { apply: t => t * 0.85 },
  vip:     { apply: t => t * 0.70 },
  none:    { apply: t => t      },
};
const discount = (kind: string, total: number) =>
  (strategies[kind] ?? strategies.none).apply(total);


// 2. Decorator: wrap a HTTP client without modifying it

interface Http { get(url: string): Promise<Response>; }

class FetchHttp implements Http {
  async get(url: string) { return fetch(url); }
}

class LoggingHttp implements Http {
  constructor(private inner: Http) {}
  async get(url: string) {
    const t = Date.now();
    try { return await this.inner.get(url); }
    finally { console.log('GET', url, Date.now() - t, 'ms'); }
  }
}

class RetryingHttp implements Http {
  constructor(private inner: Http, private max = 3) {}
  async get(url: string) {
    let err: unknown;
    for (let i = 0; i < this.max; i++) {
      try { return await this.inner.get(url); } catch (e) { err = e; }
    }
    throw err;
  }
}

const http = new LoggingHttp(new RetryingHttp(new FetchHttp(), 3));


// 3. Observer: hand-rolled event emitter

class EventBus<EventMap extends Record<string, any>> {
  private listeners = new Map<keyof EventMap, Set<(p: any) => void>>();

  on<K extends keyof EventMap>(name: K, cb: (p: EventMap[K]) => void) {
    if (!this.listeners.has(name)) this.listeners.set(name, new Set());
    this.listeners.get(name)!.add(cb as any);
    return () => this.listeners.get(name)!.delete(cb as any);
  }

  emit<K extends keyof EventMap>(name: K, payload: EventMap[K]) {
    this.listeners.get(name)?.forEach(cb => cb(payload));
  }
}

const bus = new EventBus<{ order: { id: string } }>();
bus.on('order', o => console.log('new order', o.id));
bus.emit('order', { id: 'ord_1' });

Why it matters

Patterns shine in refactors. The before/after is the unit of understanding - you see what the pattern earns. Skip patterns whose code reads worse than the original.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Fill in: class EventBus { on(evt, fn) { … } ____(evt, payload) { … } }
Try it Yourself »

Discussion

Loading…