Наследование от базовых классов

Библиотека построена вокруг иерархии классов, где базовые сущности задают общие механизмы рендеринга, управления состоянием и взаимодействия с WebGL-контекстом. Основной принцип — расширение функциональности через наследование, а не через композицию функций в процедурном стиле.

Ключевым элементом системы является класс слоя, от которого происходят все визуальные компоненты. Он инкапсулирует жизненный цикл, работу с данными и взаимодействие с GPU.


Базовый класс Layer

deck.gl реализует фундаментальную абстракцию слоя через класс Layer. Любой визуальный примитив — это наследник данного класса.

Основные задачи базового класса:

  • управление входными данными (data)
  • обработка свойств (props)
  • жизненный цикл WebGL-ресурсов
  • взаимодействие с AttributeManager
  • инициирование перерисовки при изменении состояния

Жизненный цикл слоя

Наследники Layer переопределяют ключевые методы:

class CustomLayer extends Layer {
  initializeState() {
    // создание атрибутов и буферов
  }

  updateState({ props, oldProps, changeFlags }) {
    // реакция на изменения свойств или данных
  }

  draw({ uniforms }) {
    // отрисовка через WebGL шейдеры
  }

  finalizeState() {
    // освобождение ресурсов
  }
}

initializeState вызывается один раз при создании слоя. Здесь создаются GPU-ресурсы, регистрируются атрибуты и выполняется первичная настройка.

updateState реагирует на изменения входных данных и свойств. Механизм changeFlags позволяет точно определить, что изменилось: данные, геометрия, параметры отрисовки.

draw отвечает за рендеринг. На этом этапе формируются uniform-параметры и вызываются шейдерные программы.

finalizeState освобождает WebGL-ресурсы и очищает память.


Расширение Layer и работа с состоянием

Наследование Layer предполагает строгую модель состояния. Состояние слоя хранится отдельно от props и управляется через внутренний объект state.

initializeState() {
  const { gl } = this.context;

  this.state = {
    model: this._createModel(gl)
  };
}

Состояние используется для хранения:

  • WebGL моделей
  • буферов вершин
  • кэшированных вычислений
  • промежуточных структур данных

Важно, что прямое изменение props недопустимо. Все вычисления производных данных выполняются внутри updateState.


AttributeManager и управление атрибутами

Одним из ключевых компонентов является AttributeManager. Он автоматизирует работу с vertex attributes и минимизирует ручное управление буферами.

Наследники слоя регистрируют атрибуты:

this.state.attributeManager.add({
  positions: {
    size: 3,
    accessor: 'getPosition'
  }
});

Механизм обеспечивает:

  • автоматическое обновление буферов при изменении данных
  • ленивую пересборку атрибутов
  • оптимизацию WebGL-вызовов

CompositeLayer и многоуровневое наследование

CompositeLayer — специализированный класс, предназначенный для композиции других слоёв. Он не выполняет прямой WebGL-рендеринг, а управляет набором дочерних слоёв.

Иерархия:

Layer → CompositeLayer → пользовательский составной слой

Основной метод:

renderLayers() {
  return [
    new ScatterplotLayer(...),
    new ArcLayer(...)
  ];
}

Механизм передачи props

CompositeLayer автоматически проксирует свойства дочерним слоям:

  • объединяет props
  • генерирует уникальные идентификаторы
  • управляет обновлением подслоёв

Метод _subLayerProps позволяет модифицировать параметры:

_getSubLayerProps() {
  return {
    opacity: this.props.opacity * 0.5
  };
}

Наследование и переопределение поведения

При расширении CompositeLayer переопределяется не draw, а renderLayers. Это ключевое отличие архитектуры:

class CustomCompositeLayer extends CompositeLayer {
  renderLayers() {
    const { data } = this.props;

    return data.map(item =>
      new SomeLayer({
        id: item.id,
        data: item.points
      })
    );
  }
}

Такой подход позволяет:

  • декомпозировать сложные визуализации
  • повторно использовать базовые слои
  • масштабировать сцену без усложнения WebGL-логики

Переопределение контекста и связи с View

Помимо слоёв, в архитектуре присутствуют классы представлений (View, Viewport). Они также используют наследование, но в другом контексте — управления камерой и проекцией.

Пример:

class CustomView extends View {
  makeViewport({ width, height }) {
    return new WebMercatorViewport({
      width,
      height,
      longitude: this.props.longitude,
      latitude: this.props.latitude,
      zoom: this.props.zoom
    });
  }
}

Здесь наследование используется для:

  • изменения системы координат
  • кастомизации камеры
  • управления матрицами проекции

Переиспользование логики через базовые классы

Иерархия Deck.gl строится вокруг повторного использования логики:

  • Layer — базовая графика
  • CompositeLayer — композиция слоёв
  • специализированные классы (ScatterplotLayer, PathLayer, PolygonLayer) — конкретные реализации

Каждый уровень наследования добавляет:

  • абстракцию
  • оптимизацию
  • специализацию поведения

Переопределение методов и контракт API

Наследники обязаны соблюдать контракт базового класса:

  • не нарушать сигнатуры методов
  • вызывать super при необходимости
  • не изменять напрямую внутренние структуры контекста

Пример корректного расширения:

updateState(params) {
  super.updateState(params);

  if (params.changeFlags.dataChanged) {
    this._rebuildGeometry();
  }
}

Типовые ошибки при наследовании

Нарушение модели наследования приводит к проблемам производительности и утечкам памяти:

  • создание WebGL-ресурсов вне initializeState
  • отсутствие очистки в finalizeState
  • модификация props вместо вычисления производных данных
  • обход AttributeManager и ручное управление буферами

Особенно критично создание моделей вне жизненного цикла слоя, что приводит к неконтролируемому росту GPU-ресурсов.


Расширение функциональности через многоуровневое наследование

Сложные визуализации строятся через цепочку наследников:

Layer
  → CompositeLayer
      → CustomVisualizationLayer
          → Domain-specific Layer

Каждый уровень добавляет:

  • абстракцию данных
  • трансформацию геометрии
  • визуальные эффекты
  • агрегацию слоёв

Такой подход позволяет строить масштабируемые сцены с минимальной дубликацией кода.


Связь наследования и декларативной модели

Несмотря на использование классов, Deck.gl сохраняет декларативную модель описания сцены. Наследование служит механизмом реализации, а не основным способом управления логикой.

Слой описывается как функция от props, где:

  • вход — данные и параметры
  • выход — графические примитивы WebGL

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