Шаблоны проектирования помогают решать повторяющиеся архитектурные задачи в ООП. В этом практическом руководстве вы найдёте понятные объяснения порождающих, структурных и поведенческих паттернов с примерами на TypeScript, советами по рефакторингу и ссылками на полезные ресурсы.
December 20, 2025 (8mo ago) — last updated June 18, 2026 (2mo ago)
Паттерны проектирования в ООП: практическое руководство
Изучите ключевые паттерны ООП — порождающие, структурные и поведенческие — с практическими примерами на TypeScript и советами по рефакторингу.
← Back to blog
Паттерны проектирования в ООП: практическое руководство
Освойте шаблоны проектирования в ООП с практическими примерами на TypeScript. Узнайте, как порождающие, структурные и поведенческие паттерны помогают писать поддерживаемый, расширяемый и тестируемый код.
Введение
Шаблоны проектирования — это проверенные решения для часто встречающихся задач в архитектуре ПО. Они не дают готовый код для копирования, но предлагают ясные схемы организации классов и объектов, чтобы код было проще поддерживать и расширять.
Твердое владение ключевыми паттернами и принципами ООП, такими как полиморфизм и наследование, позволяет безопасно рефакторить унаследованный код и быстрее общаться с коллегами о дизайне системы. Многие практики опираются на классический набор из 23 паттернов, описанный в «Gang of Four»1, а академические исследования подтверждают преимущества повторного использования паттернов в больших проектах2.
Что такое шаблоны проектирования в ООП
Кодирование без общей архитектуры часто приводит к запутанным решениям и высоким затратам на поддержку. Шаблоны проектирования похожи на рецепты: вы адаптируете их под конкретную задачу, чтобы получать предсказуемые и поддерживаемые результаты.

Общий язык разработчиков
Одно из главных преимуществ паттернов — общий словарь: назовите «Factory» или «Singleton», и опытный разработчик сразу поймёт замысел. Это ускоряет обсуждение архитектуры и выбор решений.
Шаблон проектирования — это описание способа решения повторяющейся задачи, которое можно адаптировать под разные ситуации.
Для практического освежения можно посмотреть сравнение полиморфизма и наследования в нашем гайде по полиморфизму и наследованию: polymorphism vs inheritance.
Три основные категории шаблонов проектирования
Gang of Four сгруппировали паттерны в три категории: порождающие, структурные и поведенческие. Знание этих категорий помогает быстро подобрать подходящий инструмент.

Обзор категорий
| Категория | Цель | Примеры |
|---|---|---|
| Порождающие | Управление созданием объектов | Factory, Builder, Singleton, Prototype |
| Структурные | Компоновка классов и объектов | Adapter, Decorator, Facade, Composite |
| Поведенческие | Взаимодействие объектов | Observer, Strategy, Command, Iterator |
Порождающие паттерны: контроль создания объектов
Порождающие паттерны вводят абстракцию над созданием объектов, чтобы клиентский код не зависел от конкретных классов.

Singleton — один экземпляр для общего ресурса
Singleton гарантирует один экземпляр класса и глобальную точку доступа. Подходит для логгера или подключения к БД, но добавляет глобальное состояние, что осложняет тестирование и управление зависимостями4.
Пример: Singleton на TypeScript для подключения к базе данных.
class DatabaseConnection {
private static instance: DatabaseConnection;
private constructor() {
// Private constructor prevents external 'new' calls
console.log("Connecting to the database...");
}
public static getInstance(): DatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
public query(sql: string): void {
console.log(`Executing query: ${sql}`);
}
}
// Usage
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
db1.query("SELECT * FROM users");
console.log(db1 === db2); // true
Компромисс Singleton: используйте его для действительно глобальных ресурсов, а для тестируемости предпочитайте внедрение зависимостей4.
Factory Method — делегирование выбора реализации
Factory Method определяет интерфейс для создания объектов, а подклассы решают, какой продукт инстанцировать. Это облегчает расширение без изменения клиентского кода.
Пример: отрисовка кнопок, специфичных для ОС, на TypeScript.
interface Button {
render(): void;
onClick(f: () => void): void;
}
class WindowsButton implements Button {
render() { console.log("Rendering a button in Windows style."); }
onClick(f: () => void) { console.log("Windows button click event."); f(); }
}
class MacButton implements Button {
render() { console.log("Rendering a button in macOS style."); }
onClick(f: () => void) { console.log("Mac button click event."); f(); }
}
abstract class Dialog {
abstract createButton(): Button;
render() {
const okButton = this.createButton();
okButton.render();
}
}
class WindowsDialog extends Dialog {
createButton(): Button { return new WindowsButton(); }
}
class MacDialog extends Dialog {
createButton(): Button { return new MacButton(); }
}
// Client
const os: string = "windows";
let dialog: Dialog;
if (os === "windows") dialog = new WindowsDialog(); else dialog = new MacDialog();
dialog.render();
Структурные паттерны: гибкая компоновка
Структурные паттерны упрощают взаимодействие компонентов и делают систему более адаптируемой.

Adapter — мост между несовместимыми интерфейсами
Adapter оборачивает один интерфейс, чтобы он соответствовал другому. Это позволяет интегрировать сторонние библиотеки без изменения существующего кода.
Пример: адаптация ModernLogger к интерфейсу ILogger.
class ModernLogger {
public logInfo(message: string): void {
console.log(`[INFO]: ${message}`);
}
}
interface ILogger { log(message: string): void; }
class LoggerAdapter implements ILogger {
private modernLogger: ModernLogger;
constructor() { this.modernLogger = new ModernLogger(); }
public log(message: string): void { this.modernLogger.logInfo(message); }
}
const logger: ILogger = new LoggerAdapter();
logger.log("User logged in successfully.");
Decorator — добавление поведения во время выполнения
Decorator оборачивает объект, добавляя функциональность динамически, что гибче наследования и соответствует принципу единственной ответственности.
Пример: компоновка подписки с дополнительными опциями.
interface Subscription { getDescription(): string; getCost(): number; }
class BasicSubscription implements Subscription {
getDescription(): string { return "Basic Plan"; }
getCost(): number { return 10; }
}
abstract class SubscriptionDecorator implements Subscription {
protected subscription: Subscription;
constructor(subscription: Subscription) { this.subscription = subscription; }
abstract getDescription(): string;
abstract getCost(): number;
}
class PremiumSupportDecorator extends SubscriptionDecorator {
getDescription(): string { return `${this.subscription.getDescription()}, Premium Support`; }
getCost(): number { return this.subscription.getCost() + 5; }
}
class CloudStorageDecorator extends SubscriptionDecorator {
getDescription(): string { return `${this.subscription.getDescription()}, 1TB Cloud Storage`; }
getCost(): number { return this.subscription.getCost() + 7; }
}
let mySubscription: Subscription = new BasicSubscription();
mySubscription = new PremiumSupportDecorator(mySubscription);
mySubscription = new CloudStorageDecorator(mySubscription);
console.log(mySubscription.getDescription());
console.log(mySubscription.getCost());
Поведенческие паттерны: организация взаимодействия
Эти паттерны управляют коммуникацией между объектами, делая поведение системы предсказуемым и модульным.
Observer — уведомления подписчикам
Observer устанавливает отношение «один-ко-многим», при котором изменения в Subject автоматически рассылаются подписчикам.
Пример: сервис уведомлений.
interface Subject { attach(observer: Observer): void; detach(observer: Observer): void; notify(): void; }
interface Observer { update(subject: Subject): void; }
class NotificationService implements Subject {
public state: string = '';
private observers: Observer[] = [];
attach(observer: Observer): void { this.observers.push(observer); }
detach(observer: Observer): void { const i = this.observers.indexOf(observer); if (i !== -1) this.observers.splice(i, 1); }
notify(): void { for (const o of this.observers) o.update(this); }
public createNewPost(title: string): void { this.state = `New Post: ${title}`; console.log(`\nNotificationService: A new post was created.`); this.notify(); }
}
class EmailNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`EmailNotifier: Sending email about "${subject.state}"`); } }
class PushNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`PushNotifier: Sending push notification for "${subject.state}"`); } }
const notificationService = new NotificationService();
const emailer = new EmailNotifier();
const pusher = new PushNotifier();
notificationService.attach(emailer);
notificationService.attach(pusher);
notificationService.createNewPost("Understanding Observer Pattern");
notificationService.detach(pusher);
notificationService.createNewPost("Why Strategy is Awesome");
Observer обеспечивает слабую связанность: Subject не зависит от конкретных реализаций Observer.
Strategy — подмена алгоритмов во время выполнения
Strategy инкапсулирует алгоритмы, чтобы выбирать их динамически, избегая громоздких условных конструкций.
Пример: стратегии оплаты для корзины.
interface PaymentStrategy { pay(amount: number): void; }
class CreditCardStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} with Credit Card.`); } }
class PayPalStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} via PayPal.`); } }
class ShoppingCart {
private paymentStrategy: PaymentStrategy;
constructor(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
public setPaymentStrategy(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
public checkout(amount: number): void { this.paymentStrategy.pay(amount); }
}
const cart = new ShoppingCart(new CreditCardStrategy());
cart.checkout(150);
cart.setPaymentStrategy(new PayPalStrategy());
cart.checkout(150);
Распространённые ошибки и стратегии рефакторинга
Не достаточно просто знать паттерн, важно не злоупотреблять ими. Избегайте анти-паттернов, таких как God Object, и следите за «запахами кода»: слишком длинные методы, избыточные зависимости, монстр-классы. Рефакторинг улучшает структуру без изменения поведения, позволяя безопасно упорядочивать унаследованные системы.
Рефакторинг в Strategy
Когда вы видите длинный switch или if/else, выделите изменчивое поведение в интерфейс Strategy и конкретные классы стратегий. Это улучшит расширяемость и тестируемость, а также поможет следовать принципам SOLID5.
Частые вопросы (Q&A)
В: С каких паттернов начать изучение?
О: Сосредоточьтесь на 5–7 ключевых паттернах: Singleton, Factory, Adapter, Decorator, Observer и Strategy. Поймите, какую проблему решает каждый паттерн и когда его применять.
В: Когда не стоит использовать паттерн?
О: Не вводите паттерн «на всякий случай». Если паттерн добавляет сложность без реальной пользы, отложите его внедрение, следуя принципу YAGNI.
В: Можно ли применять паттерны в функциональном стиле?
О: Да. Многие идеи паттернов легко транслируются в функциональный код: Strategy — это передача функции, Decorator — функции высшего порядка. Важно сохранять суть паттерна, а не форму.
В Clean Code Guy мы помогаем командам строить ПО, которое служит долго, внедряя базовые принципы в рабочие процессы. Узнайте, как наши аудиты кода и рефакторинг, готовый к использованию с ИИ, могут дать вашей команде уверенность при релизах на https://cleancodeguy.com.
ИИ пишет код.Вы делаете его долговечным.
В эпоху ускорения ИИ чистый код — это не просто хорошая практика — это разница между системами, которые масштабируются, и кодовыми базами, которые рушатся под собственным весом.