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 };
};
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ