Цепочка ответственности представляет собой поведенческий паттерн проектирования, в котором запрос проходит через последовательность обработчиков, пока один из них не возьмёт на себя ответственность за его обработку или пока цепочка не будет полностью пройдена. В контексте валидации данных этот подход позволяет выстраивать гибкие и расширяемые системы проверки входных значений, где каждое правило выполняется независимо, но в рамках общей логики обработки.
Валидация становится особенно сложной в прикладных JavaScript-системах, где данные могут поступать из различных источников: формы, API, внешних сервисов. Библиотека Validator.js предоставляет набор атомарных функций, которые легко комбинируются между собой, что делает её удобной основой для построения цепочек ответственности.
Суть цепочки ответственности в валидации заключается в том, что каждое правило проверки рассматривается как отдельное звено:
Такая структура исключает жёсткую связанность проверок и позволяет изменять порядок правил без переписывания логики.
Валидация email, например, может включать:
Каждый шаг может быть реализован как отдельный обработчик цепочки.
Библиотека Validator.js предоставляет набор функций, каждая из которых может выступать отдельным звеном:
isEmail(value) — проверка email-форматаisLength(value, options) — проверка длины строкиisEmpty(value) — проверка на пустотуisNumeric(value) — проверка числового значенияisAlphanumeric(value) — проверка символовКаждая функция возвращает булево значение, что позволяет легко встроить их в цепочку обработки.
Цепочка ответственности в JavaScript может быть реализована через массив функций или через классическую структуру обработчиков.
const validators = [
(value) => {
if (value == null || value === '') {
return { valid: false, error: 'Значение пустое' };
}
return { valid: true, value };
},
(value) => {
if (!validator.isEmail(value)) {
return { valid: false, error: 'Некорректный email' };
}
return { valid: true, value };
},
(value) => {
if (!validator.isLength(value, { min: 5 })) {
return { valid: false, error: 'Слишком короткое значение' };
}
return { valid: true, value };
}
];
function runChain(value) {
return validators.reduce((result, validatorFn) => {
if (!result.valid) return result;
return validatorFn(result.value);
}, { valid: true, value });
}
Более структурированный вариант использует классы, где каждый обработчик является узлом цепочки.
class Handler {
setNext(handler) {
this.next = handler;
return handler;
}
handle(value) {
if (this.next) {
return this.next.handle(value);
}
return { valid: true, value };
}
}
Каждый конкретный валидатор расширяет базовый обработчик:
class EmailValidator extends Handler {
handle(value) {
if (!validator.isEmail(value)) {
return { valid: false, error: 'Неверный email' };
}
return super.handle(value);
}
}
class LengthValidator extends Handler {
handle(value) {
if (!validator.isLength(value, { min: 5 })) {
return { valid: false, error: 'Недостаточная длина' };
}
return super.handle(value);
}
}
Формирование цепочки:
const email = new EmailValidator();
const length = new LengthValidator();
email.setNext(length);
email.handle('test@example.com');
Библиотека Validator.js изначально ориентирована на функциональный стиль. Её функции не хранят состояние и не зависят друг от друга. Это делает её идеальной для построения цепочек:
Пример композиции:
function validateEmail(value) {
if (validator.isEmpty(value)) {
return { valid: false, error: 'Пустое значение' };
}
if (!validator.isEmail(value)) {
return { valid: false, error: 'Неверный формат email' };
}
if (!validator.isLength(value, { max: 50 })) {
return { valid: false, error: 'Слишком длинный email' };
}
return { valid: true, value };
}
Несмотря на отсутствие формальной структуры цепочки, логика остаётся последовательной.
Одним из ключевых преимуществ подхода является возможность динамически собирать цепочку правил в зависимости от контекста.
function buildEmailChain({ strict }) {
const chain = [];
chain.push((v) => validator.isEmpty(v) ? { valid: false } : { valid: true, value: v });
chain.push((v) => validator.isEmail(v) ? { valid: true, value: v } : { valid: false });
if (strict) {
chain.push((v) =>
validator.isLength(v, { max: 30 })
? { valid: true, value: v }
: { valid: false }
);
}
return chain;
}
В цепочке ответственности важно единообразно управлять ошибками. Возможны три стратегии:
Цепочка прерывается при первой ошибке.
Все ошибки собираются в массив:
const errors = [];
function validatorFn(value) {
if (!validator.isEmail(value)) {
errors.push('Email некорректен');
}
if (!validator.isLength(value, { min: 5 })) {
errors.push('Слишком короткий email');
}
return {
valid: errors.length === 0,
errors
};
}
Некоторые проверки выполняются раньше других, формируя приоритетную цепочку.
Цепочки можно объединять и переиспользовать как строительные блоки.
const baseChain = [
(v) => validator.isEmpty(v) ? fail('empty') : pass(v),
];
const emailChain = [
...baseChain,
(v) => validator.isEmail(v) ? pass(v) : fail('email'),
];
Такой подход уменьшает дублирование и упрощает поддержку системы валидации.
В прикладных системах цепочка ответственности часто применяется при обработке данных формы:
function validateForm(data) {
const result = {
email: validateEmail(data.email),
password: validatePassword(data.password),
age: validateAge(data.age)
};
return result;
}
Каждое поле имеет собственную цепочку, но общая структура остаётся единообразной.
Цепочка ответственности позволяет добавлять новые правила без изменения существующих:
if (user.role === 'admin') {
chain.push(adminSpecificValidator);
}
Несмотря на гибкость, цепочка ответственности имеет особенности:
Библиотека Validator.js выступает не как система цепочек, а как набор атомарных проверок, из которых эти цепочки строятся. Её функции:
Это делает её универсальным инструментом для реализации паттерна цепочки ответственности в JavaScript-приложениях различного масштаба.