🔮 Паттерны Проектирования

Проверенные решения для типичных задач разработки

"Паттерны — это язык общения между разработчиками. Знание паттернов позволяет передавать сложные идеи одним словом." — Gang of Four

Основные Паттерны

🏗️ Порождающие Паттерны

Паттерны, управляющие созданием объектов

Singleton (Одиночка)

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

JavaScript
class Database {
    constructor() {
        if (Database.instance) {
            return Database.instance;
        }
        
        this.connection = null;
        Database.instance = this;
    }
    
    connect(config) {
        if (!this.connection) {
            this.connection = createConnection(config);
            console.log('Подключение к БД установлено');
        }
        return this.connection;
    }
    
    disconnect() {
        if (this.connection) {
            this.connection.close();
            this.connection = null;
            console.log('Подключение к БД закрыто');
        }
    }
}

// Использование
const db1 = new Database();
const db2 = new Database();

console.log(db1 === db2); // true - один и тот же экземпляр

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

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

JavaScript
class JediFactory {
    createJedi(type) {
        switch(type) {
            case 'guardian':
                return new JediGuardian();
            case 'consular':
                return new JediConsular();
            case 'sentinel':
                return new JediSentinel();
            default:
                throw new Error(`Unknown Jedi type: ${type}`);
        }
    }
}

class JediGuardian {
    constructor() {
        this.specialization = 'Боевые искусства';
        this.lightsaberColor = 'синий';
    }
    
    train() {
        console.log('Тренирую боевые навыки...');
    }
}

class JediConsular {
    constructor() {
        this.specialization = 'Сила';
        this.lightsaberColor = 'зелёный';
    }
    
    train() {
        console.log('Медитирую и изучаю Силу...');
    }
}

// Использование
const factory = new JediFactory();
const guardian = factory.createJedi('guardian');
const consular = factory.createJedi('consular');

guardian.train(); // Тренирую боевые навыки...
consular.train(); // Медитирую и изучаю Силу...

Builder (Строитель)

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

JavaScript
class LightsaberBuilder {
    constructor() {
        this.lightsaber = {};
    }
    
    setColor(color) {
        this.lightsaber.color = color;
        return this;
    }
    
    setHiltMaterial(material) {
        this.lightsaber.hiltMaterial = material;
        return this;
    }
    
    setCrystal(crystal) {
        this.lightsaber.crystal = crystal;
        return this;
    }
    
    setLength(length) {
        this.lightsaber.length = length;
        return this;
    }
    
    build() {
        return new Lightsaber(this.lightsaber);
    }
}

class Lightsaber {
    constructor(config) {
        this.color = config.color;
        this.hiltMaterial = config.hiltMaterial;
        this.crystal = config.crystal;
        this.length = config.length;
    }
    
    describe() {
        return `Световой меч: ${this.color}, ${this.length}см, 
                кристалл: ${this.crystal}, рукоять: ${this.hiltMaterial}`;
    }
}

// Использование
const myLightsaber = new LightsaberBuilder()
    .setColor('синий')
    .setHiltMaterial('дюрасталь')
    .setCrystal('Илум')
    .setLength(90)
    .build();

console.log(myLightsaber.describe());

🔧 Структурные Паттерны

Паттерны, описывающие способы организации классов и объектов

Adapter (Адаптер)

Преобразует интерфейс одного класса в интерфейс другого, который ожидают клиенты.

JavaScript
// Старый API
class OldPaymentSystem {
    processPayment(amount) {
        console.log(`Обработка платежа: ${amount} кредитов`);
        return { status: 'success', amount: amount };
    }
}

// Новый интерфейс
class NewPaymentSystem {
    pay(paymentData) {
        console.log(`Новая система: платёж ${paymentData.sum} кредитов`);
        return { success: true, total: paymentData.sum };
    }
}

// Адаптер
class PaymentAdapter {
    constructor(newSystem) {
        this.newSystem = newSystem;
    }
    
    processPayment(amount) {
        const result = this.newSystem.pay({ sum: amount });
        return {
            status: result.success ? 'success' : 'failed',
            amount: result.total
        };
    }
}

// Использование
const oldSystem = new OldPaymentSystem();
const newSystem = new NewPaymentSystem();
const adapter = new PaymentAdapter(newSystem);

// Оба используют одинаковый интерфейс
oldSystem.processPayment(1000);
adapter.processPayment(1000);

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

Динамически добавляет объекту новые обязанности. Является гибкой альтернативой наследованию.

JavaScript
class Jedi {
    constructor(name) {
        this.name = name;
    }
    
    getPower() {
        return 100;
    }
    
    describe() {
        return `Джедай ${this.name}, сила: ${this.getPower()}`;
    }
}

class ForceTraining {
    constructor(jedi) {
        this.jedi = jedi;
    }
    
    getPower() {
        return this.jedi.getPower() + 50;
    }
    
    describe() {
        return `${this.jedi.describe()} + Тренировка Силы`;
    }
}

class LightsaberMastery {
    constructor(jedi) {
        this.jedi = jedi;
    }
    
    getPower() {
        return this.jedi.getPower() + 30;
    }
    
    describe() {
        return `${this.jedi.describe()} + Мастерство светового меча`;
    }
}

// Использование
let padawan = new Jedi('Люк');
console.log(padawan.describe());
// Джедай Люк, сила: 100

let trainedJedi = new ForceTraining(padawan);
console.log(trainedJedi.describe());
// Джедай Люк, сила: 150 + Тренировка Силы

let master = new LightsaberMastery(trainedJedi);
console.log(master.describe());
// Джедай Люк, сила: 180 + Тренировка Силы + Мастерство светового меча

⚙️ Поведенческие Паттерны

Паттерны, определяющие алгоритмы и распределение обязанностей между объектами

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

Определяет зависимость типа "один ко многим" между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом.

JavaScript
class JediCouncil {
    constructor() {
        this.jedis = [];
        this.threatLevel = 0;
    }
    
    subscribe(jedi) {
        this.jedis.push(jedi);
    }
    
    unsubscribe(jedi) {
        this.jedis = this.jedis.filter(j => j !== jedi);
    }
    
    setThreatLevel(level) {
        console.log(`\nУровень угрозы изменён: ${level}`);
        this.threatLevel = level;
        this.notifyAll();
    }
    
    notifyAll() {
        this.jedis.forEach(jedi => jedi.update(this.threatLevel));
    }
}

class JediKnight {
    constructor(name) {
        this.name = name;
    }
    
    update(threatLevel) {
        if (threatLevel > 5) {
            console.log(`${this.name}: Готовлюсь к бою!`);
        } else {
            console.log(`${this.name}: Продолжаю медитацию.`);
        }
    }
}

// Использование
const council = new JediCouncil();

const obiWan = new JediKnight('Оби-Ван');
const yoda = new JediKnight('Йода');
const maceWindu = new JediKnight('Мэйс Винду');

council.subscribe(obiWan);
council.subscribe(yoda);
council.subscribe(maceWindu);

council.setThreatLevel(3);
council.setThreatLevel(8);

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

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

JavaScript
// Стратегии боя
class AggressiveStrategy {
    execute() {
        return 'Атакую с полной силой!';
    }
}

class DefensiveStrategy {
    execute() {
        return 'Защищаюсь и жду момента.';
    }
}

class BalancedStrategy {
    execute() {
        return 'Атакую и защищаюсь сбалансированно.';
    }
}

// Боец
class Jedi {
    constructor(name) {
        this.name = name;
        this.strategy = null;
    }
    
    setStrategy(strategy) {
        this.strategy = strategy;
    }
    
    fight() {
        if (!this.strategy) {
            return 'Стратегия не выбрана';
        }
        return `${this.name}: ${this.strategy.execute()}`;
    }
}

// Использование
const jedi = new Jedi('Энакин');

jedi.setStrategy(new AggressiveStrategy());
console.log(jedi.fight());
// Энакин: Атакую с полной силой!

jedi.setStrategy(new DefensiveStrategy());
console.log(jedi.fight());
// Энакин: Защищаюсь и жду момента.

💎 Советы по Использованию Паттернов

  • Паттерны — это не догма, а инструмент
  • Не используй паттерн без необходимости
  • Сначала реши проблему, потом подбирай паттерн
  • Знание паттернов улучшает коммуникацию в команде
  • Паттерны должны упрощать код, а не усложнять
  • Изучай контекст применения каждого паттерна