Паттерн объектного пула

Паттерн объектного пула (Object Pool) используется для управления повторным использованием объектов, особенно в графике и анимации, где создание и удаление объектов может быть дорогой операцией. В контексте Two.js, где создаются и рендерятся векторы, фигуры и группы, применение этого паттерна существенно повышает производительность при работе с большим количеством элементов на сцене.


Основные принципы паттерна

  1. Создание пула заранее Объекты создаются заранее и хранятся в пуле в состоянии «неиспользуемый». Это позволяет избежать частых вызовов конструктора и лишней нагрузки на сборщик мусора.

  2. Выдача объекта из пула При необходимости нового объекта вместо создания нового экземпляра выбирается уже существующий из пула и активируется (делается видимым и устанавливаются начальные свойства).

  3. Возврат объекта в пул После использования объект возвращается в пул, деактивируется и очищается от временных свойств, чтобы быть готовым к повторному использованию.


Реализация объектного пула для фигур Two.js

class ObjectPool {
  constructor(factory, size = 10) {
    this.pool = [];
    this.factory = factory;
    for (let i = 0; i < size; i++) {
      this.pool.push(this.factory());
    }
  }

  acquire() {
    if (this.pool.length > 0) {
      return this.pool.pop();
    }
    return this.factory();
  }

  release(obj) {
    if (obj.clear) obj.clear(); // сброс состояния объекта
    obj.visible = false; // скрыть фигуру
    this.pool.push(obj);
  }
}
  • factory — функция, создающая новый объект Two.js (например, Two.Circle или Two.Rectangle).
  • acquire() — метод получения объекта из пула.
  • release(obj) — метод возврата объекта в пул после использования.

Пример использования с Circle

const two = new Two({ width: 800, height: 600 }).appendTo(document.body);

// Фабрика объектов
function createCircle() {
  const circle = two.makeCircle(0, 0, 20);
  circle.fill = 'blue';
  circle.noStroke();
  circle.visible = false;
  return circle;
}

// Создание пула на 20 объектов
const circlePool = new ObjectPool(createCircle, 20);

// Анимация появления случайных кругов
two.bind('upd ate', function(frameCount) {
  if (frameCount % 60 === 0) { // каждые 60 кадров
    const circle = circlePool.acquire();
    circle.position.se t(Math.random() * two.width, Math.random() * two.height);
    circle.visible = true;

    setTimeout(() => {
      circlePool.release(circle);
    }, 2000); // через 2 секунды возвращаем в пул
  }
});

two.play();

Ключевые моменты:

  • Использование visible = false для деактивации фигуры вместо удаления.
  • Метод clear() помогает обнулить все свойства, которые могли измениться в процессе анимации.
  • Пул создаётся заранее и расширяется динамически, если объектов не хватает.

Расширение пула для групп объектов

Объектный пул легко адаптируется для работы с Two.Group, что позволяет управлять комплексными композициями фигур:

function createGroup() {
  const group = two.makeGroup();
  for (let i = 0; i < 5; i++) {
    const circle = two.makeCircle(i * 25, 0, 10);
    circle.fill = 'red';
    circle.noStroke();
    group.add(circle);
  }
  group.visible = false;
  return group;
}

const groupPool = new ObjectPool(createGroup, 5);

// Получение и возврат группы
const group = groupPool.acquire();
group.position.set(100, 100);
group.visible = true;

setTimeout(() => {
  groupPool.release(group);
}, 3000);
  • Группы могут содержать несколько фигур, каждая из которых управляется централизованно через пул.
  • Паттерн сохраняет производительность даже при сложных анимациях с сотнями объектов.

Оптимизация производительности

  • Минимизация создания новых объектов: пул позволяет уменьшить количество вызовов конструкторов и операций appendChild.
  • Очистка объектов перед возвратом: сброс цвета, позиции, прозрачности и трансформаций предотвращает накопление нежелательных свойств.
  • Предварительная инициализация: создание пула заранее сокращает задержки во время анимации.

Итоговые рекомендации по применению в Two.js

  • Для динамических сцен с большим количеством повторяющихся фигур объектный пул является обязательным инструментом.
  • Использовать методы acquire() и release() вместо прямого создания и удаления объектов.
  • Для сложных сцен с группами объектов и многослойными композициями создавать отдельные пулы для разных типов объектов.
  • Поддерживать размер пула, исходя из среднего количества одновременно отображаемых объектов, с возможностью динамического расширения.

Паттерн объектного пула в Two.js позволяет обеспечить стабильную производительность, минимизировать нагрузку на память и упрощает управление сложными графическими сценами.