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

Порождающие паттерны

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

Singleton

Одиночка — паттерн, гарантирующий, что у класса есть только один экземпляр, и предоставляющий к нему глобальную точку доступа.

Глобальный доступ к состоянию

    graph TD
        ClientA[Component A] --> Singleton((Store / Service))
        ClientB[Component B] --> Singleton
        ClientC[Component C] --> Singleton
    

В мире Java/C++ для этого нужно писать специальные методы getInstance() и блокировать потоки. В JavaScript всё намного проще и элегантнее.

Сам механизм ES-модулей (import/export) работает как Синглтон. Когда вы импортируете объект из файла, среда выполнения (Node.js или бандлер вроде Webpack) выполняет файл один раз, кэширует результат и отдает одну и ту же ссылку всем, кто делает import.

API Service и Store

Классический пример — сервис для работы с API или глобальное хранилище (Store). Нам не нужно создавать новый объект подключения к серверу в каждой кнопке.


// apiService.js
class ApiService {
    constructor() {
        this.baseUrl = 'https://api.example.com';
    }
    
    fetchData() {
        console.log('Fetching from ' + this.baseUrl);
    }
}

// Создаем экземпляр СРАЗУ при экспорте
const apiInstance = new ApiService();

// Экспортируем уже готовый объект (Singleton)
export default apiInstance;


// component1.js
import api from './apiService'; // Получаем ссылку #123
// component2.js
import api from './apiService'; // Получаем ту же ссылку #123

Плюсы:

  • Экономия памяти (один объект вместо тысяч).
  • Общее состояние (если вы измените настройки в одном месте, они изменятся везде).

Минусы:

  • Тесты могут влиять друг на друга, так как состояние общее. Приходится писать методы для "сброса" синглтона.
  • В JS "одиночка" живет, пока не перезагрузится страница.

3.2 Factory [Method]

Фабричный метод — определяет интерфейс для создания объекта, но позволяет подклассам (или условиям) решать, какой именно класс создавать.

Фабрика компонентов

    flowchart LR
        Input[Data Type: 'date'] --> Factory{Component Factory}
        Input2[Data Type: 'text'] --> Factory
        
        Factory --> DatePicker[< DatePicker />]
        Factory --> TextInput[< TextInput />]
    

Во Frontend мы редко используем наследование классов для фабрик. Вместо этого мы используем функции-фабрики, которые возвращают разные React-компоненты или объекты конфигурации в зависимости от входных данных (props).

Динамические формы

Представьте, что вы строите форму по JSON-конфигу с бэкенда. Вам приходит поле type: "checkbox" или type: "select". Вы не знаете заранее, какие поля будут.


// "Фабрика" полей формы
const FieldFactory = (props) => {
    switch (props.type) {
        case 'text':
            return <input type="text" {...props} />;
        case 'checkbox':
            return <input type="checkbox" checked={props.value} />;
        case 'date':
            return <DatePicker date={props.value} />;
        default:
            return null;
    }
};

// Использование
// Мы просто говорим "дай мне поле типа date", не думая о реализации
const App = () => (
    <div>
        <FieldFactory type="text" placeholder="Name" />
        <FieldFactory type="date" />
    </div>
);

Abstract Factory

Абстрактная фабрика — создает семейства взаимосвязанных объектов. Это "Фабрика фабрик".

Семейства тем Design Systems

    classDiagram
        class AbstractThemeFactory {
            <<interface>>
            +createButton()
            +createInput()
        }
        
        class DarkThemeFactory {
            +createButton() // Returns DarkButton
            +createInput() // Returns DarkInput
        }
        
        class LightThemeFactory {
            +createButton() // Returns LightButton
            +createInput() // Returns LightInput
        }
        
        AbstractThemeFactory <|.. DarkThemeFactory
        AbstractThemeFactory <|.. LightThemeFactory
    

Если обычная Фабрика создает один объект (Кнопку), то Абстрактная Фабрика создает стиль (Кнопку, Инпут, Чекбокс), которые гарантированно сочетаются друг с другом.

В React это часто реализуется через библиотеку компонентов (MUI, AntD) или через Theme Context.

Кросс-платформенная разработка

В React Native это критически важно. У вас есть абстракция AlertFactory. На iOS она должна вызывать AlertIOS, на Android — ToastAndroid, а в вебе — window.alert или красивую модалку.


// Абстракция использования (клиентский код одинаковый)
const showNotification = (factory, message) => {
    const notification = factory.createNotification();
    notification.show(message);
}

// Семейство Web
const WebFactory = {
    createNotification: () => ({ show: (msg) => alert(msg) })
};

// Семейство Mobile (условно)
const MobileFactory = {
    createNotification: () => ({ show: (msg) => Vibration.vibrate() })
};

Prototype

Прототип — создает новые объекты путем клонирования существующего (прототипа), вместо создания с нуля через конструктор.

Клонирование состояния

    graph LR
        Original[Default Config Object] -- "Clone & Modify" --> UserConfig[User Config Object]
        UserConfig -- "Clone & Modify" --> SessionConfig[Session Config]
    

JavaScript — это прототипный язык. Здесь всё построено на этом паттерне. Но в современной разработке (особенно в Redux/React) мы используем этот паттерн для Иммутабельности (Immutability).

Мы не меняем существующий объект (чтобы не сломать ре-рендеры), мы берем его как прототип, клонируем и меняем только нужное поле.

Spread Operator

Вместо сложного метода obj.clone(), мы используем Spread-оператор (...) или structuredClone().


const defaultSettings = {
    theme: 'light',
    notifications: true,
    volume: 50
};

// Паттерн Прототип в действии:
// Берем defaultSettings как основу (прототип) и создаем новый объект,
// переопределяя только theme.
const userSettings = {
    ...defaultSettings,
    theme: 'dark' 
};

console.log(userSettings.volume); // 50 (скопировано из прототипа)

Это основа работы Redux-редьюсеров и хука useState в React при работе с объектами.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ