Builder
Строитель — порождающий паттерн, который разделяет создание сложного объекта и его представление. Он позволяет конструировать объект пошагово.
Пошаговая сборка
graph LR
Start((Start)) --> Step1[Set URL]
Step1 --> Step2[Set Method]
Step2 --> Step3[Set Headers]
Step3 --> Step4[Set Body]
Step4 --> Build[".build() / .send()"]
Build --> Result[Request Object]
В Java этот паттерн спасает от конструкторов с 10 аргументами: new User(null, true, "text", null, ...). В JavaScript у нас есть именованные аргументы в объектах (destructuring), поэтому классический Builder встречается реже.
Однако, он незаменим при создании сложных конфигураций или построении запросов (Query Builders).
Fluent API и Query Builder
Самый яркий пример — построение SQL-запросов (Knex.js) или конфигурация тестов (Jest/Cypress). Вы вызываете методы цепочкой (chaining), и код читается как английское предложение.
// Классический JS объект (иногда сложно валидировать)
const requestConfig = {
url: '/api/users',
method: 'GET',
params: { sort: 'asc' }
};
// Паттерн Builder (читаемо и безопасно)
class RequestBuilder {
constructor(url) {
this.options = { url, method: 'GET' };
}
setMethod(method) {
this.options.method = method;
return this; // Возвращаем this для цепочки
}
addHeader(key, value) {
this.options.headers = { ...this.options.headers, [key]: value };
return this;
}
build() {
return this.options;
}
}
// Использование
const req = new RequestBuilder('/api/users')
.setMethod('POST')
.addHeader('Authorization', 'Bearer token')
.build();
Lazy Initialization
Ленивая загрузка — откладывание "тяжелой" операции до момента, когда её результат реально понадобится. В вебе это Святой Грааль производительности.
Загрузка по требованию
sequenceDiagram
participant User
participant Browser
participant Server
User->>Browser: Opens Home Page
Browser->>Server: Request "main.bundle.js"
Server-->>Browser: Sends Light Bundle (200kb)
Note over Browser: User sees UI instantly
User->>Browser: Clicks "Admin Dashboard"
Browser->>Server: Request "admin.chunk.js" (Lazy)
Server-->>Browser: Sends Heavy Chunk (2MB)
Browser-->>User: Shows Dashboard
Если мы загрузим весь JS-код приложения сразу, пользователь будет ждать 10 секунд белого экрана. Мы должны загружать код частями.
React.lazy и Suspense
В современном Frontend это реализуется через динамические импорты import(). React «разрезает» ваше приложение на куски автоматически.
import React, { Suspense } from 'react';
// Этот компонент НЕ загрузится в браузер, пока его не отрендерит React
const HeavyChart = React.lazy(() => import('./HeavyChart'));
const Dashboard = () => {
return (
<div>
<h1>Statistics</h1>
{/* Показываем спиннер, пока грузится файл HeavyChart.js */}
<Suspense fallback={<Spinner />}>
<HeavyChart />
</Suspense>
</div>
);
};
Также ленивая загрузка применяется для картинок и тяжелых вычислений.
Object Pool
Пул объектов — паттерн, где мы не уничтожаем объекты, а складываем их в «хранилище» для повторного использования. Это снижает нагрузку на Garbage Collector.
Жизненный цикл объекта в пуле
stateDiagram-v2
[*] --> InPool : Created at start
InPool --> InUse : "Acquire()"
InUse --> InPool : "Release()"
note right of InUse
Объект работает
в игре/анимации
end note
Важно! В обычном React/Vue разработке вы не будете использовать этот паттерн вручную. Браузеры и движки JS достаточно умны, чтобы управлять памятью за вас.
Однако, этот паттерн критически важен в:
- GameDev в браузере: создавать 100 пуль в секунду через
new Bullet()— это убийство производительности. Сборщик мусора "фризит" игру. Пули берутся из пула и возвращаются туда при попадании. - WebGL / Three.js: инициализация геометрических объектов очень дорогая.
Canvas Game
Представьте, что вы делаете игру про космический корабль.
class BulletPool {
constructor(size) {
this.pool = [];
// Заранее создаем 100 пуль
for (let i = 0; i < size; i++) {
this.pool.push({ active: false, x: 0, y: 0 });
}
}
getBullet(x, y) {
// Ищем свободную пулю, вместо создания новой
const bullet = this.pool.find(b => !b.active);
if (bullet) {
bullet.active = true;
bullet.x = x;
bullet.y = y;
return bullet;
}
return null; // Или расширяем пул
}
releaseBullet(bullet) {
// Не удаляем, просто деактивируем
bullet.active = false;
}
}
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ