← Flyweight lesson
Lab

Flyweight

// Shared: how every marker of one kind looks. Nothing changes it.
type MarkerStyle = Readonly<{ glyph: string; size: string }>;
// Local: what belongs to one place on the map.
type Place = {
  id: number; x: number; y: number; style: MarkerStyle; selected: boolean;
};

const glyphs: Record<string, string> = { hospital: 'H', school: 'S', park: 'P' };

// The flyweight factory: one style per kind of marker, made on first request.
class StyleBook {
  private styles = new Map<string, MarkerStyle>();
  get(category: string, size: string): MarkerStyle {
    const key = `${category}/${size}`;
    let style = this.styles.get(key);
    if (!style) {
      style = Object.freeze({ glyph: glyphs[category], size });
      this.styles.set(key, style);
    }
    return style;
  }
  get count(): number {
    return this.styles.size;
  }
}

// One map, one book. Place 3 is the trauma centre, drawn large.
const place = (id: number, x: number, y: number, style: MarkerStyle): Place =>
  ({ id, x, y, style, selected: false });
const book = new StyleBook();
const places = [
  place(1, 12, 40, book.get('hospital', 'small')),
  place(2, 30, 18, book.get('school', 'small')),
  place(3, 47, 62, book.get('hospital', 'large')),
  place(4, 65, 25, book.get('hospital', 'small')),
];

// Selecting a place changes that place, never the style it shares.
function selectPlace(places: Place[], id: number) {
  for (const place of places) place.selected = place.id === id;
}
selectPlace(places, 4);

const shared = places[0].style === places[3].style;
const styles = book.count;
for (const p of places) {
  const mark = p.selected ? ' (selected)' : '';
  const { glyph, size } = p.style;
  console.log(`#${p.id} ${glyph} ${size} at ${p.x},${p.y}${mark}`);
}
console.log(`${places.length} places, ${styles} styles`);
console.log(`#1 and #4 share one style: ${shared}`);