JavaRush /Курсы /Design Patterns, SQL, и Docker /Порождающие паттерны, часть 2

Порождающие паттерны, часть 2

Design Patterns, SQL, и Docker
1 уровень , 3 лекция
Открыта

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;
    }
}
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ