Довольно легко заблудиться в лабиринте сложных взаимосвязей проекта, особенно если это IT-проект и вы только начинаете. Чтобы не путаться в сотнях строк кода, стоит познакомиться с паттернами проектирования, которые пригодятся вам даже когда вы станете senior. Они демонстрируют проверенные подходы к организации классов, объектов и их взаимодействию.
Ниже семь базовых паттернов проектирования, знание которых упростит развитие и поддержку вашего кода:
- Singleton
- Factory Method
- Observer
- Strategy
- Decorator
- Adapter
- MVC
Рассмотрим каждый подробнее и приведём примеры на JavaScript.
Singleton
Singleton — простой способ обеспечить единственный экземпляр класса, к которому можно обратиться из любой части программы. Часто используют для управления конфигурацией или подключения к базе данных.
На JavaScript это выглядит так:
// Модуль для единственного экземпляра
const SingletonModule = (function() {
let instanceReference = null;
function createInstance() {
// Генерируем случайное число
return { generated: Math.random() };
}
return {
getInstance() {
if (instanceReference === null) {
instanceReference = createInstance();
}
return instanceReference;
}
};
})();
// Проверяем, что возвращается один и тот же объект
const first = SingletonModule.getInstance();
const second = SingletonModule.getInstance();
console.log(first === second); // true
console.log(first.generated, second.generated); // одинаковые значения
Singleton полезен, когда нужен единственный глобальный объект, но злоупотреблять им не стоит — чрезмерное использование усложняет тестирование.
Factory Method
Паттерн Factory Method делегирует создание объектов подклассам, тем самым инкапсулируя логику инстанцирования. Вместо прямого вызова конструктора клиент обращается к фабричному методу, который возвращает нужный тип.
Например, в мессенджере можно иметь фабрику сообщений:
class TextMessage {
send() { console.log('Отправлено текстовое сообщение'); }
}
class ImageMessage {
send() { console.log('Отправлено изображение'); }
}
class MessageFactory {
static create(type) {
switch (type) {
case 'text': return new TextMessage();
case 'image': return new ImageMessage();
default: throw new Error('Неизвестный тип сообщения');
}
}
}
// Использование
const msg1 = MessageFactory.create('text');
msg1.send(); // Отправлено текстовое сообщение
const msg2 = MessageFactory.create('image');
msg2.send(); // Отправлено изображение
Благодаря Factory Method добавить новый тип сообщения можно без изменения клиентского кода.
Observer
Observer позволяет объектам подписываться на изменения в другом объекте и получать уведомления автоматически. Например, UI-компоненты реагируют на обновления данных в модели.
class Subject {
constructor() { this.observers = []; }
subscribe(o) { this.observers.push(o); }
notify(data) { this.observers.forEach(o => o.update(data)); }
}
Strategy
Strategy даёт возможность выбирать алгоритм во время выполнения программы, инкапсулируя каждый в отдельный класс. Это делает код гибким.
// Разные стратегии
const fastStrategy = {
compress(data) { return `Fast compress: ${data}`; }
};
const slowStrategy = {
compress(data) { return `Slow compress: ${data}`; }
};
class Compressor {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
compress(data) {
return this.strategy.compress(data);
}
}
// Использование
const compressor = new Compressor(fastStrategy);
console.log(compressor.compress('file.txt')); // Fast compress: file.txt
compressor.setStrategy(slowStrategy);
console.log(compressor.compress('file.txt')); // Slow compress: file.txt
Decorator
Паттерн Decorator помогает динамически добавлять объектам новое поведение без изменения их кода. Каждый декоратор оборачивает базовый объект и дополняет его.
class Notifier {
send(message) { console.log(`Email: ${message}`); }
}
function withSMS(notifier) {
return {
send(message) {
notifier.send(message);
console.log(`SMS: ${message}`);
}
};
}
// Пример
const emailNotifier = new Notifier();
const fullNotifier = withSMS(emailNotifier);
fullNotifier.send('Привет!');
// Email: Привет!
// SMS: Привет!
Decorator идеально подходит для постепенного расширения функциональности.
Adapter
Adapter «переводит» интерфейс одного класса в тот, который ожидает клиент. Это удобно при интеграции сторонних библиотек или изменении API.
// Старый интерфейс
class OldLogger {
logInfo(msg) { console.log(`INFO: ${msg}`); }
}
// Новый сервис с другим интерфейсом
class NewLoggerService {
write(level, msg) { console.log(`${level.toUpperCase()}: ${msg}`); }
}
// Адаптер
class LoggerAdapter {
constructor(service) {
this.service = service;
}
logInfo(msg) {
this.service.write('info', msg);
}
}
// Использование
const newService = new NewLoggerService();
const logger = new LoggerAdapter(newService);
logger.logInfo('Системный запуск');
// INFO: Системный запуск
Adapter позволяет сохранить старый код без изменений и подключить новые модули.
MVC
Model-View-Controller (MVC) — архитектурный паттерн, разделяющий данные (Model), отображение (View) и логику управления (Controller). Это задаёт чёткие границы между слоями веб- и десктопных приложений. MVC упрощает тестирование и командную разработку благодаря разделению ответственности.
Эти семь паттернов — надёжный инструментарий для построения чистого и гибкого кода. Начните применять их на практике уже сегодня, и ваши проекты станут более структурированными.