Валидация данных в JavaScript редко ограничивается единичными проверками. В реальных приложениях требования к данным становятся многослойными: необходимо учитывать тип, диапазон значений, формат строк, бизнес-правила и контекст использования. Библиотека Validator.js предоставляет набор базовых функций проверки, однако в сложных сценариях возникает необходимость их композиции и переиспользования. Именно здесь применяется подход фабрики валидаторов — функция, которая динамически создаёт специализированные валидирующие функции на основе переданных параметров.
Фабрика валидаторов — это функция высшего порядка, возвращающая функцию-валидатор. Такая конструкция позволяет:
В контексте Validator.js фабрика выступает связующим слоем между низкоуровневыми методами библиотеки и прикладной логикой приложения.
Фабричный валидатор обычно возвращает функцию, принимающую проверяемое значение и возвращающую результат проверки.
import validator from 'validator';
function isEmailFactory(options = {}) {
const {
allowDisplayName = false,
requireTld = true
} = options;
return function(value) {
if (typeof value !== 'string') return false;
return validator.isEmail(value, {
allow_display_name: allowDisplayName,
require_tld: requireTld
});
};
}
const isStrictEmail = isEmailFactory({ requireTld: true });
const isFlexibleEmail = isEmailFactory({ requireTld: false });
isStrictEmail('test@mail'); // false
isStrictEmail('test@mail.com'); // true
В этом примере фабрика инкапсулирует параметры, которые влияют на поведение Validator.js, создавая специализированные версии проверки.
Ключевой механизм фабрики — замыкание. Оно позволяет сохранить конфигурацию валидатора между вызовами.
function minLengthFactory(min) {
return function(value) {
if (typeof value !== 'string') return false;
return value.length >= min;
};
}
const isPasswordValid = minLengthFactory(8);
isPasswordValid('123'); // false
isPasswordValid('12345678'); // true
Здесь min фиксируется в области видимости
функции-валидатора, что делает её переиспользуемой без повторной
передачи параметров.
Одним из ключевых преимуществ фабрики является возможность композиции простых валидаторов в сложные правила.
function composeValidators(...validators) {
return function(value) {
return validators.every(fn => fn(value));
};
}
const isNotEmpty = value => value.length > 0;
const isString = value => typeof value === 'string';
const isAlpha = value => validator.isAlpha(value);
const isValidName = composeValidators(isString, isNotEmpty, isAlpha);
isValidName('John'); // true
isValidName('John123'); // false
Validator.js здесь используется как источник атомарных проверок, а фабрика — как механизм их объединения.
В сложных системах валидатор часто зависит не только от значения, но и от внешнего контекста: конфигурации приложения, локали, бизнес-правил.
function createLocaleAwareValidator(locale) {
return function(value) {
if (locale === 'en-US') {
return validator.isPostalCode(value, 'US');
}
if (locale === 'de-DE') {
return validator.isPostalCode(value, 'DE');
}
return false;
};
}
const validatePostalCode = createLocaleAwareValidator('en-US');
validatePostalCode('10001'); // true
validatePostalCode('ABCDE'); // false
Такой подход позволяет изолировать локализационные правила внутри фабрики и не разносить их по коду приложения.
Более гибкий вариант фабрики позволяет передавать набор правил в виде конфигурации.
function createStringValidator(rules = {}) {
return function(value) {
if (typeof value !== 'string') return false;
if (rules.email && !validator.isEmail(value)) {
return false;
}
if (rules.url && !validator.isURL(value)) {
return false;
}
if (rules.noSpaces && value.includes(' ')) {
return false;
}
return true;
};
}
const validateLogin = createStringValidator({
noSpaces: true
});
const validateContact = createStringValidator({
email: true
});
Такой стиль позволяет строить декларативные схемы проверки без внешних зависимостей.
Validator.js предоставляет широкий набор функций:
isEmailisURLisNumericisLengthisIntisBooleanФабрика валидаторов не заменяет эти функции, а организует их использование.
function createNumericRangeValidator(min, max) {
return function(value) {
if (!validator.isNumeric(String(value))) return false;
const num = Number(value);
return num >= min && num <= max;
};
}
const isAgeValid = createNumericRangeValidator(18, 65);
isAgeValid(20); // true
isAgeValid(10); // false
Здесь Validator.js используется как первичный фильтр, а бизнес-логика добавляется поверх.
Фабрика может возвращать не только функцию, но и структуру с отложенной валидацией.
function lazyValidatorFactory(fn) {
return function(value) {
let cached = null;
return {
validate() {
if (cached !== null) return cached;
cached = fn(value);
return cached;
}
};
};
}
const isEmailLazy = lazyValidatorFactory(validator.isEmail);
const result = isEmailLazy('test@mail.com');
result.validate(); // true
Такой подход используется в системах, где важно избегать повторных вычислений.
Прямое использование:
validator.isEmail(value);
validator.isLength(value, { min: 8 });
Фабрика:
const isStrongPassword = createPasswordValidator();
isStrongPassword(value);
Разница заключается не в функциональности, а в архитектуре:
При проектировании валидирующих фабрик часто возникают проблемы:
Корректная фабрика должна оставаться предсказуемой и детерминированной, возвращая одинаковый результат для одинакового входа.
Фабрики могут усиливаться дополнительными слоями поведения.
function withLogging(validatorFn) {
return function(value) {
const result = validatorFn(value);
console.log('Validation result:', result);
return result;
};
}
const isEmail = withLogging(validator.isEmail);
isEmail('test@mail.com');
Такой подход позволяет добавлять наблюдаемость без изменения исходной логики.