Фабрика валидаторов

Валидация данных в JavaScript редко ограничивается единичными проверками. В реальных приложениях требования к данным становятся многослойными: необходимо учитывать тип, диапазон значений, формат строк, бизнес-правила и контекст использования. Библиотека Validator.js предоставляет набор базовых функций проверки, однако в сложных сценариях возникает необходимость их композиции и переиспользования. Именно здесь применяется подход фабрики валидаторов — функция, которая динамически создаёт специализированные валидирующие функции на основе переданных параметров.

Концепция фабрики валидаторов

Фабрика валидаторов — это функция высшего порядка, возвращающая функцию-валидатор. Такая конструкция позволяет:

  • параметризовать правила проверки;
  • переиспользовать логику без дублирования кода;
  • создавать сложные проверки из простых примитивов;
  • инкапсулировать зависимости от 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 как слоем примитивов

Validator.js предоставляет широкий набор функций:

  • isEmail
  • isURL
  • isNumeric
  • isLength
  • isInt
  • isBoolean

Фабрика валидаторов не заменяет эти функции, а организует их использование.

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.js

Прямое использование:

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');

Такой подход позволяет добавлять наблюдаемость без изменения исходной логики.