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.
Discussion
Loading…