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

Поведенческие паттерны, часть 2

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

State

Состояние — паттерн, который позволяет объекту менять свое поведение при изменении внутреннего состояния. Объект "переключается" в другой режим работы.

Конечный автомат

    stateDiagram-v2
        [*] --> Idle
        Idle --> Loading : Fetch Data
        Loading --> Success : Data Received
        Loading --> Error : Network Fail
        Error --> Loading : Retry
        Success --> [*]
    

Во Frontend это один из самых главных паттернов. Мы постоянно управляем состоянием UI: isLoading, isError, isEmpty. Вместо кучи if-else мы используем концепцию Конечного автомата.

XState и useReducer

В React мы используем useReducer для управления сложной логикой переходов.


// Описание возможных состояний и переходов
const videoReducer = (state, action) => {
    switch (state) {
        case 'playing':
            return action.type === 'PAUSE' ? 'paused' : state;
        case 'paused':
            return action.type === 'PLAY' ? 'playing' : state;
        default:
            return state;
    }
};

// Компонент меняет своё поведение (иконку) в зависимости от State
const VideoPlayer = () => {
    const [status, dispatch] = useReducer(videoReducer, 'paused');
    
    return (
        <button onClick={() => dispatch({ type: status === 'playing' ? 'PAUSE' : 'PLAY' })}>
            {status === 'playing' ? 'Stop' : 'Play'}
        </button>
    );
};

Strategy

Стратегия — определяет семейство алгоритмов, инкапсулирует их и делает взаимозаменяемыми. Это позволяет выбирать алгоритм "на лету".

Выбор алгоритма

    graph LR
        Context[Payment Processor]
        Context --> StrategyA[Credit Card Strategy]
        Context --> StrategyB[PayPal Strategy]
        Context --> StrategyC[Apple Pay Strategy]
        
        note left of Context: Один интерфейс .pay()\nРазные реализации
    

Этот паттерн спасает от гигантских конструкций switch-case. Если у вас есть 5 способов сортировки товаров или 3 способа валидации форм — это Стратегия.

Валидация форм

Представьте, что вы пишете библиотеку для валидации. Вы создаете "Стратегии" для разных типов проверок.


// Стратегии
const strategies = {
    required: (text) => text.length > 0 ? null : "Field is required",
    email: (text) => text.includes('@') ? null : "Invalid email",
    minLength: (text) => text.length >= 5 ? null : "Too short"
};

// Контекст (Функция валидации)
const validate = (value, ruleName) => {
    // Мы просто берем нужную функцию из словаря
    return strategies[ruleName](value);
};

// Использование
validate("abc", "email"); // "Invalid email"
validate("hello@world.com", "email"); // null

Template Method

Шаблонный метод — определяет "скелет" алгоритма в базовом классе, но позволяет наследникам переопределять отдельные шаги, не меняя структуру алгоритма.

Скелет компонента

    graph TD
        Base[Abstract List Component] --> Step1[Fetch Data (Common)]
        Base --> Step2[Render Item (Abstract)]
        Base --> Step3[Show Footer (Common)]
        
        Step2 -.-> ImplA[User Item Renderer]
        Step2 -.-> ImplB[Product Item Renderer]
    

В ООП это наследование. В React мы используем Render Props или Slots. Мы создаем компонент "Список", который умеет загружать данные и скроллить, но то, как рисовать отдельную строку, мы отдаем на откуп родителю.

Render Props


// "Шаблонный" компонент
const List = ({ data, renderItem }) => (
    <ul>
        {data.map((item) => (
            <li key={item.id}>
                {/* Шаг алгоритма, который определяет внешний код */}
                {renderItem(item)}
            </li>
        ))}
    </ul>
);

// Использование
<List 
    data={users} 
    renderItem={(user) => <b>User: {user.name}</b>} 
/>

6.4 Chain of Responsibility

Цепочка обязанностей — передает запрос по цепочке обработчиков. Каждый обработчик решает: обработать запрос самому или передать следующему.

Конвейер обработки

    graph LR
        Request --> Handler1[Logger]
        Handler1 --> Handler2[Auth Check]
        Handler2 --> Handler3[Validation]
        Handler3 --> Handler4[Controller]
        
        Handler2 -- "Error (401)" --> Response
    

Это основа работы любого веб-сервера Express.js, Koa, NestJS и фронтенд-инструментов Redux Middleware, Axios Interceptors.

Middleware

В Redux каждое действие проходит через цепочку middleware перед тем, как попасть в редьюсер.


// Обработчик 1: Логирование
const logger = store => next => action => {
    console.log('Action:', action);
    return next(action); // Передаем дальше
};

// Обработчик 2: Аналитика
const analytics = store => next => action => {
    if (action.type === 'BUY') trackPurchase();
    return next(action); // Передаем дальше
};

// Цепочка
const middlewareChain = [logger, analytics];

Memento

Хранитель — позволяет сохранить состояние объекта, чтобы потом его восстановить.

Снимок времени

    timeline
        title History of Changes
        Step 1 : Text: "H"
        Step 2 : Text: "He"
        Step 3 : Text: "Hel"
        Step 4 : Text: "Hello"
        Current : Text: "Hello W"
    

В вебе этот паттерн часто реализуется через сериализацию в JSON. Вы сохраняете состояние формы в localStorage, чтобы при перезагрузке страницы пользователь не потерял введенные данные.

Time Travel Debugging

Redux DevTools реализуют этот паттерн идеально. Они хранят массив всех состояний приложения (снимков), и вы можете ползунком перемещаться во времени, возвращая приложение в прошлое.


// Простое Undo
const useUndo = (initialState) => {
    const [history, setHistory] = useState([initialState]);
    
    const push = (newState) => setHistory([...history, newState]);
    
    const undo = () => {
        if (history.length > 1) {
            setHistory(history.slice(0, -1)); // Удаляем последний снимок
        }
    };
    
    const current = history[history.length - 1];
    return { current, push, undo };
};
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ