Wrapper / Boxing — Обёртка / Боксинг

Функция-обёртка над функцией или класс-контейнер над значением. В GoF отдельного паттерна нет — это семейство приёмов, близких к Adapter и Decorator.

Проблема

  • Добавить поведение к функции (логирование, профилирование, мемоизация) — Wrapper.
  • Превратить примитив в объект с валидацией/методами (Email, Phone, Money) — Boxing.

Решение

Wrapper (HOF): функция принимает функцию, возвращает функцию с добавленным поведением.

Boxing: класс или объект, оборачивающий примитив. В JS все примитивы боксированы автоматически ('str'.length работает за счёт автобоксинга в String).

Пример в JS

// Wrapper — функция-обёртка
const wrap = (before, fn, after) =>
  (...args) => after(fn(...before(...args)));

const sum = (a, b) => a + b;
const wrapped = wrap(
  (a, b) => [a * 2, b * 2],
  sum,
  (r) => r + 1,
);
wrapped(1, 2); // ((1*2)+(2*2))+1 = 7
// Boxing — класс-контейнер со своей валидацией
class Email {
  constructor(value) {
    if (!value.includes('@')) throw new Error('invalid email');
    this.value = value;
  }
  domain { return this.value.split('@')[1]; }
}

const e = new Email('test@example.com');
e.domain; // 'example.com'

Где используется в JS-экосистеме

  • DDD Value Objects: Money, Email, PhoneNumber — типичный boxing
  • Decorators в Node: util.debuglog(name) возвращает wrapped function
  • HOF: _.throttle, _.debounce, _.memoize — wrapper-ы
  • Autoboxing: (42).toString() — Number boxing на лету

Подводные камни

  • Wrapper теряет fn.length — обёрнутая функция «не знает», сколько аргументов брала исходная.
  • Wrapper теряет fn.name — стек-трейсы становятся менее читаемыми (нужно Object.defineProperty(wrapped, 'name', ...)).
  • Boxing vs Adapter: Adapter стыкует разные контракты; Boxing просто оборачивает.
  • Boxing vs Decorator: Decorator работает с тем же интерфейсом и расширяет; Boxing добавляет интерфейс к примитиву.

Главные тезисы автора

  • «Wrapper в GoF нет, но в JS это чуть ли не синоним адаптера».
  • Wrapper — это HOF: принимает функцию, возвращает функцию.
  • before/after-перехватчики — универсальная схема обёртки.
  • Для async-функций перехватчиков четыре: до вызова, после вызова, до callback, после callback.
  • «В JavaScript боксирование везде» — все примитивы боксированы.
  • Свой бокс полезен для валидации в конструкторе: телефон, email, адрес.
  • factorify, poolify, promisify — все построены на идее wrapper.

🎓 Источники

См. также