← Composite lesson
Lab

Composite

type Action = 'pause' | 'resume';

// What a valve and a zone have in common: one command.
interface IrrigationPart {
  readonly id: string;
  command(action: Action, trace: string[]): void;
}

class Valve implements IrrigationPart {
  paused = false;
  constructor(readonly id: string) {}
  command(action: Action, trace: string[]): void {
    trace.push(this.id);
    this.paused = action === 'pause';
  }
}

class Zone implements IrrigationPart {
  constructor(readonly id: string, readonly children: IrrigationPart[]) {}
  command(action: Action, trace: string[]): void {
    trace.push(this.id);
    for (const child of this.children) child.command(action, trace);
  }
}

// The controller: one command to whatever the operator selected.
function send(part: IrrigationPart, action: Action): string[] {
  const trace: string[] = [];
  part.command(action, trace);
  return trace;
}

const nursery = new Zone('nursery', [new Valve('propagation-valve')]);
const west = new Zone('west', [new Valve('east-valve'), nursery]);
const house = new Zone('house-1', [new Valve('north-valve'), west]);
const bench = new Valve('bench-valve'); // the potting bench, in no zone

// A leak report pauses the whole house; maintenance pauses one valve.
const leak = send(house, 'pause');
const maintenance = send(bench, 'pause');
console.log(`leak: ${leak.join(' > ')}`);
console.log(`maintenance: ${maintenance.join(' > ')}`);