Backend Typescript 1.0.0 Help

Patterns

πŸ“‘ ОглавлСниС

Π”ΠΈΠ°Π³Ρ€Π°ΠΌΠΌΠ° соотвСтствий

ΠšΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΡ

ΠŸΠ°Ρ‚Ρ‚Π΅Ρ€Π½

Когда ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡ‚ΡŒ (симптом)

ΠŸΠΎΡ€ΠΎΠΆΠ΄Π°ΡŽΡ‰ΠΈΠ΅

Singleton

НуТСн СдинствСнный ΠΎΠ±ΡŠΠ΅ΠΊΡ‚, глобальная Ρ‚ΠΎΡ‡ΠΊΠ° доступа, лСнивая инициализация

Factory Method

Много подклассов ΠΎΠ΄Π½ΠΎΠ³ΠΎ сСмСйства; Π²Ρ‹Π±ΠΎΡ€ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΎΡ‚Π»ΠΎΠΆΠ΅Π½ Π² подкласс

Abstract Factory

БогласованныС сСмСйства ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΎΠ² (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, GUI ΠΏΠΎΠ΄ Ρ€Π°Π·Π½Ρ‹Π΅ ОБ)

Builder

БлоТная пошаговая сборка ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²; Π½ΡƒΠΆΠ½Ρ‹ Ρ€Π°Π·Π½Ρ‹Π΅ прСдставлСния ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚Π°

Prototype

ЧастоС ΠΊΠ»ΠΎΠ½ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΡ…ΠΎΠΆΠΈΡ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², Π΄ΠΎΡ€ΠΎΠ³ΠΎ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ «Ρ нуля»

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π½Ρ‹Π΅

Adapter

ΠŸΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ нСсовмСстимыС интСрфСйсы («ΠΎΠ±Ρ‘Ρ€Ρ‚ΠΊΠ°»)

Facade

Π‘ΠΏΡ€ΡΡ‚Π°Ρ‚ΡŒ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ подсистСмы Π·Π° простой API

Decorator

ДинамичСски Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Π±Π΅Π· наслСдования

Composite

Π˜Π΅Ρ€Π°Ρ€Ρ…ΠΈΡ «Ρ‡Π°ΡΡ‚ΡŒ-Ρ†Π΅Π»ΠΎΠ΅»: Π΄Π΅Ρ€Π΅Π²ΠΎ, Π³Π΄Π΅ лист ΠΈ ΡƒΠ·Π΅Π» ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°ΡŽΡ‚ΡΡ ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²ΠΎ

Proxy

ΠšΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒ доступа, кэш, лСнивая Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ°, ΡƒΠ΄Π°Π»Ρ‘Π½Π½Ρ‹ΠΉ доступ

ΠŸΠΎΠ²Π΅Π΄Π΅Π½Ρ‡Π΅ΡΠΊΠΈΠ΅

Strategy

БСмСйство Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ², Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΌΠ΅Π½ΡΡ‚ΡŒ Π²ΠΎ врСмя исполнСния

Observer

Подписка/ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ мноТСства ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΎΠ± измСнСниях

Command

Π˜Π½ΠΊΠ°ΠΏΡΡƒΠ»ΡΡ†ΠΈΡ запроса ΠΊΠ°ΠΊ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°; ΠΎΡ‚ΠΌΠ΅Π½Π°/история

State

Π Π°Π·Π½Ρ‹Π΅ состояния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° → Ρ€Π°Π·Π½Ρ‹Π΅ повСдСния

Chain of Responsibility

Гибкая ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° запроса ΠΏΠΎ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ²

ΠŸΠΎΡ€ΠΎΠΆΠ΄Π°ΡŽΡ‰ΠΈΠ΅

1) Singleton

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: НуТСн СдинствСнный ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ (Π½Π°ΠΏΡ€. ΠΊΠΎΠ½Ρ„ΠΈΠ³/рССстр/кСш) ΠΈ Сдиная Ρ‚ΠΎΡ‡ΠΊΠ° доступа.
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Один ΠΈ Ρ‚ΠΎΡ‚ ΠΆΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ создаётся Π² Ρ€Π°Π·Π½Ρ‹Ρ… мСстах, рассылаСтся ΠΏΠΎ всСму ΠΊΠΎΠ΄Ρƒ Ρ‡Π΅Ρ€Π΅Π· ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹/Π³Π»ΠΎΠ±Π°Π»ΠΈ.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

// Π’Π΅Π·Π΄Π΅ new Config(), состояния Ρ€Π°ΡΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΡƒΡŽΡ‚ΡΡ class Config { constructor(public env = process.env.NODE_ENV ?? 'prod') { } } const a = new Config(); const b = new Config(); // Π΄Ρ€ΡƒΠ³ΠΎΠ΅ мСсто console.log(a.env === b.env); // ΠΌΠΎΠΆΠ΅Ρ‚ ΡΠΎΠ²ΠΏΠ°ΡΡ‚ΡŒ случайно, Π½ΠΎ это Ρ€Π°Π·Π½Ρ‹Π΅ экзСмпляры

РСшСниС: ΠŸΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ конструктор + статичСский Π³Π΅Ρ‚Ρ‚Π΅Ρ€, Π»Π΅Π½ΠΈΠ²ΠΎ ΡΠΎΠ·Π΄Π°ΡŽΡ‰ΠΈΠΉ СдинствСнный экзСмпляр.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

class Config { private static instance?: Config; private constructor(public readonly env = process.env.NODE_ENV ?? 'prod') {} static getInstance(): Config { if(!Config.instance){ Config.instance = new Config() }; return Config.instance; } } const cfg = Config.getInstance();

2) Factory Method

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: Π’ΠΈΠΏ создаваСмого ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° Π·Π°Ρ€Π°Π½Π΅Π΅ нСизвСстСн, Π½ΠΎ Ρƒ Π½Π΅Π³ΠΎ Π΅ΡΡ‚ΡŒ ΠΎΠ±Ρ‰ΠΈΠΉ интСрфСйс.
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Π‘ΠΎΠ»ΡŒΡˆΠΈΠ΅ switch/if ΠΏΠΎ Ρ‚ΠΈΠΏΡƒ ΠΏΡ€ΠΈ создании.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

type PayType = 'card' | 'cash' | 'crypto'; function createPayment(type: PayType) { if (type === 'card') return { pay: (a: number) => console.log('card', a) }; if (type === 'cash') return { pay: (a: number) => console.log('cash', a) }; if (type === 'crypto') return { pay: (a: number) => console.log('crypto', a) }; throw new Error('Unknown'); }

РСшСниС: Π”Π΅Π»Π΅Π³ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ созданиС подклассам/классам-создатСлям.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

abstract class Payment { abstract pay(amount: number): void; } class CardPayment extends Payment { pay(a: number) { console.log(`ΠšΠ°Ρ€Ρ‚Π°: ${a}β‚½`); } } class CashPayment extends Payment { pay(a: number) { console.log(`НаличныС: ${a}β‚½`); } } abstract class PaymentCreator { abstract create(): Payment; } class CardPaymentCreator extends PaymentCreator { create() { return new CardPayment(); } } const p = new CardPaymentCreator().create(); p.pay(1999);

3) Abstract Factory

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: НуТны «ΡΠΎΠ³Π»Π°ΡΠΎΠ²Π°Π½Π½Ρ‹Π΅» сСмСйства ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΎΠ² (Π½Π°ΠΏΡ€., Π²ΠΈΠ΄ΠΆΠ΅Ρ‚Ρ‹ GUI ΠΏΠΎΠ΄ Win/Mac).
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Π’ ΠΊΠΎΠ΄Π΅ ΠΌΠ½ΠΎΠ³ΠΎ if (platform === 'win') Π²ΠΎΠΊΡ€ΡƒΠ³ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

function renderUI(platform: 'win' | 'mac') { const btn = platform === 'win' ? 'WinButton' : 'MacButton'; const chk = platform === 'win' ? 'WinCheckbox' : 'MacCheckbox'; // Π΄ΡƒΠ±Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π»ΠΎΠ³ΠΈΠΊΠΈ Π²Ρ‹Π±ΠΎΡ€Π° ΠΏΠΎ ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΠ΅ Π²Π΅Π·Π΄Π΅ }

РСшСниС: ВвСсти Ρ„Π°Π±Ρ€ΠΈΠΊΡƒ сСмСйства ΠΈ Π²Ρ‹Π±ΠΈΡ€Π°Ρ‚ΡŒ Ρ„Π°Π±Ρ€ΠΈΠΊΡƒ ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

interface Button { render(): string; } interface Checkbox { check(): string; } class WinButton implements Button { render() { return 'WinButton'; } } class MacButton implements Button { render() { return 'MacButton'; } } class WinCheckbox implements Checkbox { check() { return 'WinCheckbox'; } } class MacCheckbox implements Checkbox { check() { return 'MacCheckbox'; } } interface GUIFactory { createButton(): Button; createCheckbox(): Checkbox; } class WinFactory implements GUIFactory { createButton() { return new WinButton(); } createCheckbox() { return new WinCheckbox(); } } class MacFactory implements GUIFactory { createButton() { return new MacButton(); } createCheckbox() { return new MacCheckbox(); } } function buildUI(factory: GUIFactory) { const btn = factory.createButton(); const chk = factory.createCheckbox(); console.log(btn.render(), chk.check()); } buildUI(new WinFactory());

4) Builder

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: ΠžΠ±ΡŠΠ΅ΠΊΡ‚ слоТный (ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎΠ»Π΅ΠΉ, ΠΎΠΏΡ†ΠΈΠΉ), конструктор разрастаСтся.
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: ΠšΠΎΠ½ΡΡ‚Ρ€ΡƒΠΊΡ‚ΠΎΡ€Ρ‹ с 10+ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ, мноТСство undefined/null.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

// Ρ‚Ρ€ΡƒΠ΄Π½ΠΎ Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ ΠΈ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒ const report = new Report("Sales", true, undefined, undefined, "RU", 20, null, "A4", /* ... */);

РСшСниС: Пошаговая сборка Ρ‡Π΅Ρ€Π΅Π· Ρ†Π΅ΠΏΠΎΡ‡ΠΊΡƒ Π²Ρ‹Π·ΠΎΠ²ΠΎΠ².

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

class Query { constructor( public select = '*', public from?: string, public where?: string ) {} toString() { return `SELECT ${this.select} FROM ${this.from} ${this.where ? ` WHERE ${this.where}` : ''}`; } } class QueryBuilder { private q = new Query(); select(cols: string) { this.q.select = cols; return this; } from(tbl: string) { this.q.from = tbl; return this; } where(cond: string) { this.q.where = cond; return this; } build() { return this.q; } } const sql = new QueryBuilder() .from('users') .where('active=1') .build();

5) Prototype

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: Часто Π½ΡƒΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ ΠΊΠΎΠΏΠΈΠΈ ΡƒΠΆΠ΅ настроСнных ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ².
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Π ΡƒΡ‡Π½ΠΎΠ΅ ΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΠ»Π΅ΠΉ ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ создании.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

const baseTpl = { title: 'Π‘Ρ‡Ρ‘Ρ‚', vat: 20, footer: 'Бпасибо' }; function cloneInvoiceTpl() { return { title: baseTpl.title, vat: baseTpl.vat, footer: baseTpl.footer }; }

РСшСниС: ΠžΠ±ΡŠΠ΅ΠΊΡ‚ ΡƒΠΌΠ΅Π΅Ρ‚ сам сСбя ΠΊΠ»ΠΎΠ½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

interface Clonable<T> { clone(): T; } class InvoiceTemplate implements Clonable<InvoiceTemplate> { constructor( public title: string, public vat: number, public footer: string, public subTemplate?: InvoiceTemplate ) {} clone() { return new InvoiceTemplate( this.title, this.vat, this.footer, this.subTemplate ? subTemplate.clone() : undefined ); } } const tpl = new InvoiceTemplate('Π‘Ρ‡Ρ‘Ρ‚', 20, 'Бпасибо'); const tpl2 = tpl.clone();

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π½Ρ‹Π΅

6) Adapter

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: Π’Π½Π΅ΡˆΠ½ΠΈΠΉ сСрвис ΠΈΠΌΠ΅Π΅Ρ‚ «Π½Π΅ΡƒΠ΄ΠΎΠ±Π½Ρ‹ΠΉ» API.
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Π’ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Π²Π΅Π·Π΄Π΅ ΠΌΠ΅Π»ΠΊΠΈΠ΅ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ ΠΊ этому API.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

// ΠšΠ»ΠΈΠ΅Π½Ρ‚ Π·Π½Π°Π΅Ρ‚ Π΄Π΅Ρ‚Π°Π»ΠΈ старого API class OldBankAPI { sendMoney(sum: number) { /* ... */ } } function pay(amount: number, bank: OldBankAPI) { bank.sendMoney(amount); // связали ΠΊΠ»ΠΈΠ΅Π½Ρ‚ со старым API }

РСшСниС: ВвСсти Ρ†Π΅Π»Π΅Π²ΠΎΠΉ интСрфСйс ΠΈ Π°Π΄Π°ΠΏΡ‚Π΅Ρ€ ΠΊ старому API.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

class CardSystem { sendMoney(sum: number) { console.log('CardSystem bank:', sum); } } class CryptoSystem { sendCripto(hash: number) { console.log('CryptoSystem bank:', hash); } } class CardCryptoSystemAdapter extends CardSystem { constructor(private readonly system: CryptoSystem) sendMoney(sum: number) { const hash = btoa(sum); this.system.sendCripto(hash) } } class Bank{ constructor(private system: CardSystem) {} pay(amount: number) { this.system.sendMoney(amount); } }

7) Facade

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: ΠšΠ»ΠΈΠ΅Π½Ρ‚Ρƒ приходится Π·Π½Π°Ρ‚ΡŒ ΠΌΠ½ΠΎΠ³ΠΎ Π΄Π΅Ρ‚Π°Π»Π΅ΠΉ подсистСмы.
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Много Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² Π½ΠΈΠ·ΠΊΠΎΡƒΡ€ΠΎΠ²Π½Π΅Π²Ρ‹Ρ… ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² ΠΈΠ· ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

const cpu = new CPU(); const mem = new Memory(); const disk = new Disk(); const data = disk.read(); mem.load(0, data); cpu.start(); // ΠΊΠ»ΠΈΠ΅Π½Ρ‚ управляСт всСм процСссом

РСшСниС: Π‘ΠΏΡ€ΡΡ‚Π°Ρ‚ΡŒ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π·Π° простой API фасада.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

class CPU { start() { console.log('CPU start'); } } class Memory { load(addr: number, data: string) { console.log('load', addr, data); } } class Disk { read() { return 'OS'; } } class ComputerFacade { constructor( private cpu = new CPU(), private mem = new Memory(), private disk = new Disk() ) {} boot() { this.mem.load(0, this.disk.read()); this.cpu.start(); } } new ComputerFacade().boot();

8) Decorator

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: НуТно динамичСски Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ Ρ„ΠΈΡ‡ΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρƒ.
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: ΠœΠ½ΠΎΠΆΠ΅ΡΡ‚Π²ΠΎ подклассов «ΠšΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΡΠ+Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅+КСш+Аудит».

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

class OrderServiceWithLoggingAndCachingAndMetrics { /* ... ΠΌΠΎΠ½ΠΎΠ»ΠΈΡ‚ ... */ }

РСшСниС: ΠžΠ±ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°Ρ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Π² ΠΎΠ΄ΠΈΠ½ ΠΈΠ»ΠΈ нСсколько Π΄Π΅ΠΊΠΎΡ€Π°Ρ‚ΠΎΡ€ΠΎΠ².

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

interface Notifier { send(msg: string): void; } class BaseNotifier implements Notifier { send(msg: string) { console.log('Base:', msg); } } class SMSDecorator implements Notifier { constructor(private wrappee: Notifier) {} send(msg: string) { this.wrappee.send(msg); console.log('SMS:', msg); } } class EmailDecorator implements Notifier { constructor(private wrappee: Notifier) {} send(msg: string) { this.wrappee.send(msg); console.log('Email:', msg); } } const notifier = new EmailDecorator(new SMSDecorator(new BaseNotifier())); notifier.send('ΠŸΠΎΡΡ‚ΡƒΠΏΠΈΠ»Π° заявка');

9) Composite

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: НуТно ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²ΠΎ ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ Π΄Π΅Ρ€Π΅Π²ΠΎ (ΠΏΠ°ΠΏΠΊΠΈ/Ρ„Π°ΠΉΠ»Ρ‹, мСню/ΠΏΡƒΠ½ΠΊΡ‚Ρ‹).
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Π’ ΠΊΠΎΠ΄Π΅ Π²Π΅Π·Π΄Π΅ if (leaf) else (node).

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

type Node = { name: string; children?: Node[] }; function print(n: Node) { if (!n.children) console.log(n.name); else { console.log(n.name); n.children.forEach(print); } }

РСшСниС: Лист ΠΈ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΡŽΡ‚ ΠΎΠ±Ρ‰ΠΈΠΉ интСрфСйс.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

interface Component { show(indent?: string): void; } class File implements Component { constructor(private name: string) {} show(indent = '') { console.log(indent + this.name); } } class Folder implements Component { private children: Component[] = []; constructor(private name: string) {} add(c: Component) { this.children.push(c); } show(indent = '') { console.log(indent + this.name); this.children.forEach((c) => c.show(indent + ' ')); } } const root = new Folder('root');// /root root.add(new File('a.txt')); // /root/a.txt const docs = new Folder('docs');// /roo docs.add(new File('readme.md')); root.add(docs); root.show();

10) Proxy

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: НуТСн ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒ доступа/кСш/лСнивая Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π²ΠΎΠΊΡ€ΡƒΠ³ сСрвиса.
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: КСш/ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ ΠΏΡ€Π°Π² Π΄ΡƒΠ±Π»ΠΈΡ€ΡƒΡŽΡ‚ΡΡ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ мСстС Π²Ρ‹Π·ΠΎΠ²Π°.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

const data = service.getData(); if (!cache["data"]) cache["data"] = data; // Π΄ΡƒΠ±Π»ΠΈΡ€ΡƒΠ΅ΠΌ Π² ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π»Π΅Ρ€Π°Ρ…, сСрвисах ΠΈ Ρ‚.Π΄.

РСшСниС: ВвСсти «Π·Π°ΠΌΠ΅ΡΡ‚ΠΈΡ‚Π΅Π»Ρ», ΠΈΠ½ΠΊΠ°ΠΏΡΡƒΠ»ΠΈΡ€ΡƒΡŽΡ‰Π΅Π³ΠΎ ΡΠΊΠ²ΠΎΠ·Π½ΡƒΡŽ Π»ΠΎΠ³ΠΈΠΊΡƒ.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

interface Service { getData(): string; } class RealService implements Service { getData() { return 'сСкрСт'; } } class ServiceProxy implements Service { private cache?: string; constructor(private real = new RealService()) {} getData() { if (!this.cache) { this.cache = this.real.getData(); console.log('real'); } else console.log('cache'); return this.cache; } } const svc = new ServiceProxy(); svc.getData(); // real svc.getData(); // cache

ΠŸΠΎΠ²Π΅Π΄Π΅Π½Ρ‡Π΅ΡΠΊΠΈΠ΅

11) Strategy

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: НуТно ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΡ‹ (сортировка, Ρ†Π΅Π½ΠΎΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅, доставка).
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Π‘ΠΎΠ»ΡŒΡˆΠΈΠ΅ if/else ΠΏΠΎ Ρ‚ΠΈΠΏΡƒ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ°.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

function price(base: number, region: 'EU' | 'US' | 'RU') { if (region === 'EU') return base * 1.2; if (region === 'US') return base * 1.1; return base * 1.05; }

РСшСниС: Π’Ρ‹Π΄Π΅Π»ΠΈΡ‚ΡŒ стратСгии, ΠΏΠΎΠ΄ΡΡ‚Π°Π²Π»ΡΡ‚ΡŒ Π½ΡƒΠΆΠ½ΡƒΡŽ Π²ΠΎ врСмя исполнСния.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

interface PricingStrategy { calc(base: number): number; } enum PricingType { EU, US } class EUPricing implements PricingStrategy { calc(b: number) { return b * 1.2; } } class USPricing implements PricingStrategy { calc(b: number) { return b * 1.1; } } class Checkout { constructor(private readonly strategyTable: Map<PricingType, PricingStrategy>) {} total(type: PricingType, base: number) { return this.strategyTable.get(type)!.calc(base); } } const checkout = new Checkout( new Map<PricingType, PricingStrategy>([ [PricingType.EU, new EUPricing()], [PricingType.US, new USPricing()] ]) ); checkout.total(PricingType.EU, 3000);

12) Observer

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: Много подписчиков Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Ρ€Π΅Π°Π³ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ (события, курсы Π²Π°Π»ΡŽΡ‚, ΠΈΠ½Π²Π΅Π½Ρ‚Π°Ρ€ΡŒ).
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Π ΡƒΡ‡Π½Ρ‹Π΅ Π²Ρ‹Π·ΠΎΠ²Ρ‹ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ зависимого ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

function onPriceChange(v:number){ updateUI(v); sendAnalytics(v); notifyPartners(v); // растёт список Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² }

РСшСниС: МодСль «ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠ°-ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅».

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

type Listener = (v: number) => void; class Subject { private observers = new Set<Listener>(); subscribe(l: Listener) { this.observers.add(l); } unsubscribe(l: Listener) { this.observers.delete(l); } notify(v: number) { this.observers.forEach((l) => l(v)); } } const price$ = new Subject(); price$.subscribe((v) => console.log('UI:', v)); price$.subscribe((v) => console.log('Analytics:', v)); price$.notify(120);

13) Command

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: НуТны undo/redo, ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ Π·Π°Π΄Π°Ρ‡, Π»ΠΎΠ³ Π°ΡƒΠ΄ΠΈΡ‚Π°.
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: ДСйствия Π²Ρ‹Π·Ρ‹Π²Π°ΡŽΡ‚ΡΡ Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ Π±Π΅Π· истории.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

class Cart { items:string[]=[]; add(i:string){ this.items.push(i);} } const cart = new Cart(); cart.add("Π’Π΅Π»Π΅Ρ„ΠΎΠ½"); // Как ΠΎΡ‚ΠΊΠ°Ρ‚ΠΈΡ‚ΡŒ? Π˜ΡΡ‚ΠΎΡ€ΠΈΠΈ Π½Π΅Ρ‚.

РСшСниС: Π˜Π½ΠΊΠ°ΠΏΡΡƒΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ дСйствия Π² ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ ΠΈ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ ΠΈΡΡ‚ΠΎΡ€ΠΈΡŽ.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

interface Command { execute(): void; undo(): void; } class Cart { items: string[] = []; add(i: string) { this.items.push(i); } remove(i: string) { this.items = this.items.filter((x) => x !== i); } } class AddItemCommand implements Command { constructor( private cart: Cart, private item: string ) {} execute() { this.cart.add(this.item); } undo() { this.cart.remove(this.item); } } class Invoker { private history: Command[] = []; run(c: Command) { c.execute(); this.history.push(c); } undo() { this.history.pop()?.undo(); } } const inv = new Invoker(); const cart2 = new Cart(); inv.run(new AddItemCommand(cart2, 'Π’Π΅Π»Π΅Ρ„ΠΎΠ½')); inv.undo();

14) State

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: ПовСдСниС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° зависит ΠΎΡ‚ состояния (статус Π·Π°ΠΊΠ°Π·Π°, Π΄Π²Π΅Ρ€ΡŒ, Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚).
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Гигантский switch(state) Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

type DoorState = 'locked' | 'unlocked'; let state: DoorState = 'locked'; function click() { if (state === 'locked') { console.log('open'); state = 'unlocked'; } else { console.log('close'); state = 'locked'; } }

РСшСниС: ВынСсти состояния Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ классы, контСкст Π΄Π΅Π»Π΅Π³ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

interface State { handle(ctx: Context): void; } class Context { constructor(public state: State) {} set(s: State) { this.state = s; } request() { this.state.handle(this); } } class Locked implements State { handle(ctx: Context) { console.log('ΠžΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ'); ctx.set(new Unlocked()); } } class Unlocked implements State { handle(ctx: Context) { console.log('Π—Π°ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ'); ctx.set(new Locked()); } } const door = new Context(new Locked()); door.request(); door.request();

15) Chain of Responsibility

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°: Запрос Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΡ€ΠΎΠΉΡ‚ΠΈ Ρ‡Π΅Ρ€Π΅Π· Ρ†Π΅ΠΏΠΎΡ‡ΠΊΡƒ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΎΠΊ (аутСнтификация, валидация, бизнСс-ΠΏΡ€Π°Π²ΠΈΠ»Π°).
Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ: Π’Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Π΅ if, Ρ€Π°Π·ΠΌΠ°Π·Π°Π½Π½Ρ‹Π΅ ΠΏΠΎ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρƒ.

ΠŸΠ»ΠΎΡ…ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ (симптом Π² ΠΊΠΎΠ΄Π΅):

function handle(req:string){ if (!req.includes("auth")) return "401"; if (!req.includes("payload")) return "400"; return "200"; }

РСшСниС: Π‘Π±ΠΎΡ€ΠΊΠ° Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² — ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Π»ΠΈΠ±ΠΎ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅Ρ‚, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘Ρ‚ дальшС.

Код Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ:

abstract class Handler { private next?: Handler; setNext(h: Handler) { this.next = h; return h; } handle(req: string): string | undefined { return this.next?.handle(req); } } class AuthHandler extends Handler { handle(req: string) { if (!req.includes('auth')) return '401'; return super.handle(req); } } class ValidationHandler extends Handler { handle(req: string) { if (!req.includes('payload')) return '400'; return super.handle(req) ?? '200'; } } // {} -> {} -> {} -> {} const pipeline = new AuthHandler(); pipeline.setNext(new ValidationHandler()); console.log(pipeline.handle('auth+payload')); // 200 console.log(pipeline.handle('payload')); // 401
Last modified: 01 October 2025