Паттерны проектирования в JavaScript с примерами для Node.js
Паттерны проектирования — это проверенные решения типовых задач разработки. В JavaScript, особенно в среде Node.js, они помогают создавать масштабируемый, поддерживаемый и переиспользуемый код. В этой статье мы разберём четыре основных паттерна: Singleton, Factory, Observer и Module, с практическими примерами.
1. Паттерн Singleton (Одиночка)
Singleton гарантирует, что класс имеет только один экземпляр, и предоставляет глобальную точку доступа к нему. В Node.js это часто используется для подключения к базе данных или конфигурации приложения.
Реализация Singleton в Node.js
class Database {
constructor() {
if (Database.instance) {
return Database.instance;
}
this.connection = null;
Database.instance = this;
}
async connect(uri) {
// Имитация подключения к БД
this.connection = `Connected to ${uri}`;
console.log(this.connection);
}
getConnection() {
return this.connection;
}
}
// Использование
const db1 = new Database();
const db2 = new Database();
console.log(db1 === db2); // true
db1.connect('mongodb://localhost:27017/myapp');
console.log(db2.getConnection()); // "Connected to mongodb://localhost:27017/myapp"
Когда использовать: Управление пулом соединений, глобальный логгер, кэш-системы.
2. Паттерн Factory (Фабрика)
Factory позволяет создавать объекты без указания конкретного класса. Он централизует логику создания объектов, упрощая поддержку кода.
Реализация Factory для валидаторов
class EmailValidator {
validate(value) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}
}
class PhoneValidator {
validate(value) {
return /^\+?[\d\s\-()]{7,15}$/.test(value);
}
}
class ValidatorFactory {
createValidator(type) {
switch (type) {
case 'email':
return new EmailValidator();
case 'phone':
return new PhoneValidator();
default:
throw new Error('Unknown validator type');
}
}
}
// Использование
const factory = new ValidatorFactory();
const emailValidator = factory.createValidator('email');
console.log(emailValidator.validate('test@example.com')); // true
console.log(emailValidator.validate('invalid')); // false
Когда использовать: Создание сложных объектов, работа с разными форматами данных (JSON, XML, YAML), генерация UI-компонентов.
3. Паттерн Observer (Наблюдатель)
Observer создаёт механизм подписки, позволяющий одним объектам (наблюдателям) следить за изменениями в другом объекте (субъекте). В Node.js этот паттерн встроен через класс EventEmitter.
Реализация Observer с EventEmitter
const EventEmitter = require('events');
class NewsPublisher extends EventEmitter {
publishArticle(title) {
console.log(`📰 Опубликована статья: ${title}`);
this.emit('article', { title, date: new Date() });
}
}
class Subscriber {
constructor(name) {
this.name = name;
}
receiveArticle(article) {
console.log(`🔔 ${this.name} получил: "${article.title}"`);
}
}
// Использование
const publisher = new NewsPublisher();
const subscriber1 = new Subscriber('Анна');
const subscriber2 = new Subscriber('Иван');
publisher.on('article', (data) => subscriber1.receiveArticle(data));
publisher.on('article', (data) => subscriber2.receiveArticle(data));
publisher.publishArticle('Паттерны проектирования в JavaScript');
// Вывод:
// 📰 Опубликована статья: Паттерны проектирования в JavaScript
// 🔔 Анна получил: "Паттерны проектирования в JavaScript"
// 🔔 Иван получил: "Паттерны проектирования в JavaScript"
Когда использовать: Обработка событий, real-time приложения (чаты, уведомления), системы мониторинга.
4. Паттерн Module (Модуль)
Module — один из самых популярных паттернов в JavaScript. Он инкапсулирует приватные данные и методы, предоставляя только публичный API. В Node.js модули реализованы через require и module.exports.
Реализация Module для работы с пользователями
// userManager.js
const users = []; // Приватная переменная
function validateUser(user) {
return user.name && user.email;
}
module.exports = {
addUser(user) {
if (!validateUser(user)) {
throw new Error('Invalid user data');
}
users.push(user);
return `User ${user.name} added`;
},
getUsers() {
return [...users]; // Возвращаем копию
},
findByName(name) {
return users.find(user => user.name === name) || null;
},
get count() {
return users.length;
}
};
// Использование в другом файле
const userManager = require('./userManager');
userManager.addUser({ name: 'Алексей', email: 'alex@example.com' });
userManager.addUser({ name: 'Мария', email: 'maria@example.com' });
console.log(userManager.count); // 2
console.log(userManager.getUsers());
// [{ name: 'Алексей', email: 'alex@example.com' }, { name: 'Мария', email: 'maria@example.com' }]
console.log(userManager.findByName('Мария')); // { name: 'Мария', email: 'maria@example.com' }
Когда использовать: Практически любой JavaScript-код. Это основа для организации Node.js приложений.
Сравнение паттернов и рекомендации
Выбор паттерна зависит от конкретной задачи:
- Singleton — когда нужен ровно один экземпляр (БД, конфиг).
- Factory — когда создание объектов сложное или зависит от условий.
- Observer — для событийно-ориентированной архитектуры.
- Module — для организации кода и сокрытия деталей реализации.
Заключение
Паттерны проектирования — это не догма, а инструменты. Используйте их осознанно, не усложняя код там, где достаточно простого решения. В JavaScript и Node.js особенно популярны модульная система и событийная модель, что делает паттерны Observer и Module естественным выбором для большинства приложений.
Освоив эти четыре паттерна, вы получите прочную базу для дальнейшего изучения более сложных шаблонов, таких как Proxy, Decorator или Strategy. Практикуйтесь на реальных проектах, и паттерны станут вашим надёжным помощником в разработке.