Паттерны проектирования в JavaScript с примерами: от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Паттерны проектирования в JavaScript с примерами: от А до Я

Паттерны проектирования (design patterns) — это проверенные решения типовых задач, возникающих при разработке программного обеспечения. В JavaScript, особенно в среде Node.js, понимание этих шаблонов помогает писать более чистый, модульный и масштабируемый код. В этой статье мы разберём основные категории паттернов с наглядными примерами.

1. Порождающие паттерны (Creational Patterns)

Эти паттерны управляют процессом создания объектов, делая систему независимой от способа их инстанцирования.

1.1. Одиночка (Singleton)

Гарантирует, что класс имеет только один экземпляр, и предоставляет глобальную точку доступа к нему. В Node.js модули по умолчанию кэшируются, что упрощает реализацию.

class DatabaseConnection {
  constructor() {
    if (DatabaseConnection.instance) {
      return DatabaseConnection.instance;
    }
    this.connection = null;
    DatabaseConnection.instance = this;
  }

  connect() {
    if (!this.connection) {
      this.connection = { status: 'connected' };
      console.log('Новое подключение к БД');
    }
    return this.connection;
  }
}

const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // true

1.2. Фабричный метод (Factory Method)

Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. Идеально подходит для создания разных типов логгеров.

class LoggerFactory {
  createLogger(type) {
    switch (type) {
      case 'console':
        return new ConsoleLogger();
      case 'file':
        return new FileLogger();
      default:
        throw new Error('Неизвестный тип логгера');
    }
  }
}

class ConsoleLogger {
  log(message) { console.log(message); }
}

class FileLogger {
  log(message) { console.log(`[Файл]: ${message}`); }
}

const factory = new LoggerFactory();
const logger = factory.createLogger('console');
logger.log('Тестовое сообщение');

2. Структурные паттерны (Structural Patterns)

Описывают способы объединения классов и объектов для создания более крупных структур.

2.1. Декоратор (Decorator)

Динамически добавляет объекту новые обязанности. В JavaScript декораторы часто реализуются через функции высшего порядка или прототипы.

class Coffee {
  cost() { return 10; }
  description() { return 'Кофе'; }
}

function withMilk(coffee) {
  coffee.cost = () => coffee.cost() + 5;
  coffee.description = () => coffee.description() + ', с молоком';
  return coffee;
}

function withSugar(coffee) {
  coffee.cost = () => coffee.cost() + 2;
  coffee.description = () => coffee.description() + ', с сахаром';
  return coffee;
}

let myCoffee = new Coffee();
myCoffee = withMilk(myCoffee);
myCoffee = withSugar(myCoffee);
console.log(`${myCoffee.description()}: ${myCoffee.cost()} руб.`); // Кофе, с молоком, с сахаром: 17 руб.

2.2. Адаптер (Adapter)

Преобразует интерфейс одного класса в другой интерфейс, ожидаемый клиентом. Особенно полезен при интеграции сторонних библиотек.

class OldAPI {
  getUserData() {
    return { name: 'Иван', age: 25, email: 'ivan@example.com' };
  }
}

class NewAPI {
  fetchUser() {
    return { fullName: 'Иван Петров', yearsOld: 26, contact: 'ivan@example.com' };
  }
}

class UserAdapter {
  constructor(api) {
    this.api = api;
  }

  getUserData() {
    const data = this.api.fetchUser();
    return {
      name: data.fullName,
      age: data.yearsOld,
      email: data.contact
    };
  }
}

const adapter = new UserAdapter(new NewAPI());
console.log(adapter.getUserData()); // { name: 'Иван Петров', age: 26, email: 'ivan@example.com' }

3. Поведенческие паттерны (Behavioral Patterns)

Определяют алгоритмы и способы взаимодействия между объектами.

3.1. Наблюдатель (Observer)

Создаёт механизм подписки, позволяющий одним объектам следить за изменениями в других. Основа событийной модели Node.js.

class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }

  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(listener => listener(data));
    }
  }
}

const emitter = new EventEmitter();
emitter.on('userLogin', (user) => {
  console.log(`Пользователь ${user.name} вошёл в систему`);
});
emitter.emit('userLogin', { name: 'Анна' });

3.2. Стратегия (Strategy)

Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Отлично подходит для реализации различных способов сортировки или сжатия данных.

class Sorter {
  constructor(strategy) {
    this.strategy = strategy;
  }

  sort(data) {
    return this.strategy(data);
  }
}

const bubbleSort = (arr) => {
  const sorted = [...arr];
  for (let i = 0; i < sorted.length; i++) {
    for (let j = 0; j < sorted.length - i - 1; j++) {
      if (sorted[j] > sorted[j + 1]) {
        [sorted[j], sorted[j + 1]] = [sorted[j + 1], sorted[j]];
      }
    }
  }
  return sorted;
};

const quickSort = (arr) => {
  if (arr.length <= 1) return arr;
  const pivot = arr[0];
  const left = arr.slice(1).filter(x => x < pivot);
  const right = arr.slice(1).filter(x => x >= pivot);
  return [...quickSort(left), pivot, ...quickSort(right)];
};

const sorter = new Sorter(bubbleSort);
console.log(sorter.sort([5, 3, 8, 1, 2])); // [1, 2, 3, 5, 8]
sorter.strategy = quickSort;
console.log(sorter.sort([5, 3, 8, 1, 2])); // [1, 2, 3, 5, 8]

4. Практические рекомендации для Node.js

  • Не злоупотребляйте Singleton — он усложняет тестирование. Используйте только для действительно глобальных ресурсов (пулы соединений, конфигурация).
  • Observer встроен в Node.js — модуль events предоставляет готовый EventEmitter. Используйте его для собственных событий.
  • Factory Method упрощает конфигурацию

Блоги

Рекомендаций по книгам