← Chain of responsibility lesson
Lab

Chain of responsibility

// A lost-property claim, and what one desk decides about it.
type Claim = { caseId: string; item: string; seenAt: string };
type Decision = 'pass' | 'claimed' | 'blocked';
type Desk = { name: string; handle: (claim: Claim) => Decision };
type Outcome = { status: string; desk: string; checked: string[] };

// Security keeps what it confiscated at screening: those claims stop here.
const security: Desk = {
  name: 'security',
  handle: (claim) => (claim.seenAt === 'screening' ? 'blocked' : 'pass'),
};
const terminal: Desk = {
  name: 'terminal',
  handle: (claim) => (claim.seenAt === 'terminal' ? 'claimed' : 'pass'),
};
const airline: Desk = {
  name: 'airline',
  handle: (claim) => (claim.seenAt === 'airline' ? 'claimed' : 'pass'),
};
// The central store takes whatever reaches it.
const central: Desk = { name: 'central', handle: () => 'claimed' };

// Offer the claim to each desk in order; the first claim or block ends it.
function routeClaim(claim: Claim, desks: Desk[]): Outcome {
  const checked: string[] = [];
  for (const desk of desks) {
    const decision = desk.handle(claim);
    checked.push(desk.name);
    if (decision !== 'pass') {
      return { status: decision, desk: desk.name, checked };
    }
  }
  return { status: 'unhandled', desk: 'nobody', checked };
}

// Route one claim and print where it ended up.
function report(claim: Claim, desks: Desk[]) {
  const outcome = routeClaim(claim, desks);
  const where = `${outcome.status} by ${outcome.desk}`;
  const path = outcome.checked.join(' > ');
  console.log(`${claim.caseId} ${claim.item}: ${where}; checked ${path}`);
}

// The intake owner chooses which desks are open, and their order.
const day = [security, terminal, airline, central];
const night = [security, terminal, airline]; // the central store is closed

const pouch = { caseId: 'LP-202', item: 'camera pouch', seenAt: 'airline' };
const powerBank = { caseId: 'LP-203', item: 'power bank', seenAt: 'screening' };
const umbrella = { caseId: 'LP-205', item: 'umbrella', seenAt: 'gate 12' };
report(pouch, day);
report(powerBank, day);
report(umbrella, night);