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

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

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

Начать курс

Паттерны проектирования в TypeScript: от простого к сложному

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

В этой статье мы разберем ключевые паттерны проектирования, их реализацию на TypeScript и практические примеры использования. Материал будет полезен как начинающим разработчикам, так и опытным специалистам, желающим систематизировать знания.

Порождающие паттерны: Singleton и Factory Method

Порождающие паттерны отвечают за создание объектов, скрывая логику инстанцирования. Рассмотрим два самых популярных представителя: Singleton и Factory Method.

Singleton (Одиночка)

Singleton гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. В TypeScript реализация становится особенно элегантной благодаря статическим членам и приватным конструкторам.

class DatabaseConnection {
    private static instance: DatabaseConnection;
    private connectionString: string;

    private constructor(connectionString: string) {
        this.connectionString = connectionString;
        console.log(`Создано соединение с: ${connectionString}`);
    }

    static getInstance(connectionString: string): DatabaseConnection {
        if (!DatabaseConnection.instance) {
            DatabaseConnection.instance = new DatabaseConnection(connectionString);
        }
        return DatabaseConnection.instance;
    }

    query(sql: string): void {
        console.log(`Выполнение запроса: ${sql} на ${this.connectionString}`);
    }
}

// Использование
const db1 = DatabaseConnection.getInstance('postgres://localhost:5432/mydb');
const db2 = DatabaseConnection.getInstance('postgres://localhost:5432/mydb');
console.log(db1 === db2); // true - один и тот же объект

Этот паттерн идеально подходит для управления ресурсами: пулы соединений, логирование, кэширование. Однако будьте осторожны — Singleton может усложнить тестирование из-за глобального состояния.

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

Factory Method определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. В TypeScript это реализуется через абстрактные классы или интерфейсы.

interface Notification {
    send(message: string): void;
}

class EmailNotification implements Notification {
    send(message: string): void {
        console.log(`Отправка email: ${message}`);
    }
}

class SMSNotification implements Notification {
    send(message: string): void {
        console.log(`Отправка SMS: ${message}`);
    }
}

abstract class NotificationFactory {
    abstract createNotification(): Notification;

    notify(message: string): void {
        const notification = this.createNotification();
        notification.send(message);
    }
}

class EmailFactory extends NotificationFactory {
    createNotification(): Notification {
        return new EmailNotification();
    }
}

class SMSFactory extends NotificationFactory {
    createNotification(): Notification {
        return new SMSNotification();
    }
}

// Использование
const factory: NotificationFactory = new EmailFactory();
factory.notify('Ваш заказ подтвержден!');

Factory Method помогает соблюдать принцип открытости/закрытости (OCP) — вы можете добавлять новые типы уведомлений без изменения существующего кода.

Структурные паттерны: Adapter и Decorator

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

Adapter (Адаптер)

Adapter позволяет объектам с несовместимыми интерфейсами работать вместе. В TypeScript это часто используется при интеграции сторонних библиотек или работе с устаревшим кодом.

interface ModernAPI {
    request(): string;
}

class LegacySystem {
    specificRequest(): string {
        return 'Legacy data format';
    }
}

class LegacyAdapter implements ModernAPI {
    private legacySystem: LegacySystem;

    constructor(legacySystem: LegacySystem) {
        this.legacySystem = legacySystem;
    }

    request(): string {
        const result = this.legacySystem.specificRequest();
        return `Адаптировано: ${result}`;
    }
}

// Использование
const legacy = new LegacySystem();
const adapter = new LegacyAdapter(legacy);
console.log(adapter.request()); // "Адаптировано: Legacy data format"

Adapter — это «мостик» между старым и новым кодом. Он позволяет постепенно мигрировать систему без полного рефакторинга.

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

Decorator динамически добавляет объекту новые обязанности. В TypeScript декораторы — это встроенная языковая возможность (экспериментальная), но мы рассмотрим классическую реализацию паттерна.

interface Coffee {
    cost(): number;
    description(): string;
}

class SimpleCoffee implements Coffee {
    cost(): number {
        return 10;
    }

    description(): string {
        return 'Обычный кофе';
    }
}

class MilkDecorator implements Coffee {
    private coffee: Coffee;

    constructor(coffee: Coffee) {
        this.coffee = coffee;
    }

    cost(): number {
        return this.coffee.cost() + 3;
    }

    description(): string {
        return `${this.coffee.description()}, с молоком`;
    }
}

class SugarDecorator implements Coffee {
    private coffee: Coffee;

    constructor(coffee: Coffee) {
        this.coffee = coffee;
    }

    cost(): number {
        return this.coffee.cost() + 2;
    }

    description(): string {
        return `${this.coffee.description()}, с сахаром`;
    }
}

// Использование
let coffee: Coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(`${coffee.description()}: ${coffee.cost()} руб.`);
// "Обычный кофе, с молоком, с сахаром: 15 руб."

Decorator — мощный инструмент для создания гибких систем, где функциональность можно комбинировать как конструктор Lego.

Поведенческие паттерны: Observer и Strategy

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

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

Observer создает механизм подписки, позволяющий одним объектам следить за изменениями в других. TypeScript с его строгой типизацией делает реализацию особенно надежной.

interface Observer {
    update(subject: Subject): void;
}

interface Subject {
    attach(observer: Observer): void;
    detach(observer: Observer): void;
    notify(): void;
}

class WeatherStation implements Subject {
    private observers: Observer[] = [];
    private temperature: number = 0;

    attach(observer: Observer): void {
        this.observers.push(observer);
    }

    detach(observer: Observer): void {
        this.observers = this.observers.filter(obs => obs !== observer);
    }

    notify(): void {
        for (const observer of this.observers) {
            observer.update(this);
        }
    }

    setTemperature(temp: number): void {
        this.temperature = temp;
        this.notify();
    }

    getTemperature(): number {
        return this                

Блоги

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