Коротка відповідь
middleFlyweight — це паттерн, що зменшує кількість об’єктів шляхом спільного використання одних і тих же внутрішніх станів. Ключова частина — фабрика, що кешує «легковесні» об’єкти; зовнішній стан передається як параметр. Приклад: `const getTree = (type)=>{ if(!cache[type]) cache[type]=new Tree(type); return cache[type]; }`. Такі об’єкти можна використовувати в графічних редакторах, іграх чи списках.
Повне пояснення
1. Що це і навіщо
Flyweight (приспособленец) — структурний паттерн, що зменшує кількість створюваних об’єктів шляхом спільного використання одних і тих же внутрішніх даних. Підходить, коли існує велика кількість об’єктів із однаковим внутрішнім станом, а зовнішній стан змінюється.
2. Принцип роботи / алгоритм
Паттерн розділяє стан на intrinsic (спільний, незмінний) і extrinsic (зовнішній, змінюваний). Фабрика створює/повертає об’єкт‑легковес, що містить лише intrinsic. При використанні передається extrinsic через параметри методу.
3. Реалізація (JS / TS)
- Базова реалізація
class Tree {
constructor(private type: string) {}
draw(x: number, y: number) { console.log(`Draw ${this.type} at (${x},${y})`); }
}
const cache: Record<string, Tree> = {};
function getTree(type: string): Tree {
if (!cache[type]) cache[type] = new Tree(type);
return cache[type];
}
- Реалістичний приклад (графічний редактор)
class Shape {
constructor(private color: string) {}
render(ctx: CanvasRenderingContext2D, x: number, y: number) {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(x, y, 5, 0, Math.PI * 2);
ctx.fill();
}
}
const shapeCache: Record<string, Shape> = {};
function getShape(color: string): Shape {
if (!shapeCache[color]) shapeCache[color] = new Shape(color);
return shapeCache[color];
}
// usage: shapes.forEach(s => getShape(s.color).render(ctx, s.x, s.y));
- Спрощений для новачка (список символів)
class Char {
constructor(private char: string) {}
print() { process.stdout.write(this.char); }
}
const charCache: Record<string, Char> = {};
function getChar(c: string): Char {
if (!charCache[c]) charCache[c] = new Char(c);
return charCache[c];
}
4. Тестування (Jest або Vitest)
import { getTree } from './flyweight';
test('reuse same tree instance', () => {
const t1 = getTree('oak');
const t2 = getTree('oak');
expect(t1).toBe(t2);
});
5. Проблеми та edge cases
- Неправильне розділення стану – якщо extrinsic помилково ставиться в intrinsic, об’єкти стають некоректними.
- Зайва абстракція – створення фабрики для міні-об’єктів, що не потребують кешу.
- Cyclical dependencies – фабрика і об’єкт залежать один від одного, що ускладнює тестування.
- Плутанина з Prototype – об’єкти, що копіюються, а не спільно використовуються.
- Performance‑пастка – надмірна кількість об’єктів у кеші, що не очищується, збільшує пам’ять.
6. Оптимізація та продуктивність
Кешування зменшує кількість конструкторів, що економить CPU і пам’ять. Однак якщо кеш не очищується, він може стати «memory leak». Використання слабких посилань (WeakMap) допомагає уникнути зайвих об’єктів.
7. Без JS / архітектурні альтернативи
У веб‑інтерфейсі можна досягти подібної ефективності через CSS клас (intrinsic) і data‑атрибути (extrinsic). У бекенді – через singleton‑сервіси або object pooling.
8. Cheatsheet + follow‑up
- Cheatsheet: Фабрика → кеш → getInstance(type) → об’єкт з intrinsic. Передайте extrinsic у метод.
- Interview Q1: Як розділити intrinsic і extrinsic?
- Interview Q2: Які ризики пов’язані з кешуванням у Flyweight?