SmartQuery /ВОПРОС → ПРИМЕР → ПОНЯТНЫЙ ОТВЕТ
Языки и платформы

Flyweight в TypeScript: общие неизменяемые данные и отдельные координаты

Flyweight разделяет повторяющиеся данные между множеством объектов. Общая часть, intrinsic state, не должна меняться для одного потребителя. Координаты и другое индивидуальное extrinsic state остаются снаружи. Выигрыш возможен, когда дублирование действительно существенно; сам паттерн ускорения не гарантирует.

Фабрика общего описания

type Texture = Readonly<{ color: string; pattern: string }>;

class TextureFactory {
  private readonly cache = new Map<string, Texture>();

  get(color: string, pattern: string): Texture {
    const key = JSON.stringify([color, pattern]);
    let texture = this.cache.get(key);
    if (!texture) {
      texture = Object.freeze({ color, pattern });
      this.cache.set(key, texture);
    }
    return texture;
  }
}

type Sprite = { x: number; y: number; texture: Texture };
const factory = new TextureFactory();
const first: Sprite = {
  x: 10, y: 20, texture: factory.get("green", "dots"),
};
const second: Sprite = {
  x: 70, y: 20, texture: factory.get("green", "dots"),
};
console.log(first.texture === second.texture); // true
first.x = 100;
console.log(second.x); // 70

Фабрика хранит один объект для одной пары color/pattern. JSON-массив в ключе не путает пары из-за выбранного вручную разделителя. Map не интерпретирует пользовательский ключ как унаследованное свойство объекта, как могло случиться с обычным объектом-кешем.

Что защищает readonly, а что freeze

Readonly помогает TypeScript обнаружить попытку присвоения при проверке типов. Object.freeze ограничивает изменение свойств во время выполнения. Обе защиты здесь неглубокие; это достаточно для двух строковых полей, но вложенный изменяемый объект не стал бы автоматически неизменяемым.

Проверки перед применением

  • Одинаковое intrinsic state возвращает ту же ссылку; разные параметры не должны случайно разделять объект.
  • Изменение координат одного Sprite не меняет координаты другого.
  • Общие данные не содержат идентификатор пользователя, координаты или другой индивидуальный контекст.
  • Фабрика имеет понятный срок жизни. Неограниченный Map с бесконечным набором ключей способен удерживать память дольше самих потребителей.

Пример показывает структуру паттерна, а не измеренную экономию памяти. Профилируйте реальную нагрузку: для крошечных объектов служебные ключи и Map могут стоить больше, чем дублирование. Иногда достаточно обычного кеша или небольшого заранее подготовленного набора ресурсов.

Документация