├── .gitignore ├── README.md ├── behavioral_patterns ├── Chain_of_command.ts ├── Command.ts ├── Iterator.ts ├── Mediator.ts ├── Observer.ts ├── State.ts ├── Strategy.ts └── Template_method.ts ├── creational_patterns ├── Builder.ts ├── Factory.ts ├── Prototype.ts └── Singleton.ts └── structure_patterns ├── Adapter.ts ├── Bridge.ts ├── Composite.ts ├── Facade.ts └── Proxy.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Шаблоны проектирования 2 | 3 | Шаблоны проектирования помогают нам унифицировать код под уже существующие лучшие практики. Используя нужный шаблон проектирования в определёной ситуации формирует стерильный код. Здесь описаны в общей сумме 17 паттернов: порождающие, структурные и поведенческие. 4 | 5 | ## Оглавление 6 | - [Шаблоны проектирования](#шаблоны-проектирования) 7 | - [Оглавление](#оглавление) 8 | - [Порождающие паттерны](#порождающие-паттерны) 9 | - [Singleton (Одиночка)](#singleton-одиночка) 10 | - [Предназначение](#предназначение) 11 | - [Суть паттерна](#суть-паттерна) 12 | - [Пример реализации](#пример-реализации) 13 | - [Prototype (Прототип)](#prototype-прототип) 14 | - [Предназначение](#предназначение-1) 15 | - [Суть паттерна](#суть-паттерна-1) 16 | - [Пример реализации](#пример-реализации-1) 17 | - [Builder (Строитель)](#builder-строитель) 18 | - [Предназначение](#предназначение-2) 19 | - [Суть паттерна](#суть-паттерна-2) 20 | - [Пример реализации](#пример-реализации-2) 21 | - [Factory (Фабрика)](#factory-фабрика) 22 | - [Предназначение](#предназначение-3) 23 | - [Суть паттерна](#суть-паттерна-3) 24 | - [Пример реализации](#пример-реализации-3) 25 | - [Структурные паттерны:](#структурные-паттерны) 26 | - [Bridge (Мост)](#bridge-мост) 27 | - [Предназначение](#предназначение-4) 28 | - [Суть паттерна](#суть-паттерна-4) 29 | - [Пример реализации](#пример-реализации-4) 30 | - [Facade (Фасад)](#facade-фасад) 31 | - [Предназначение](#предназначение-5) 32 | - [Суть паттерна](#суть-паттерна-5) 33 | - [Пример реализации](#пример-реализации-5) 34 | - [Adapter (Адаптер)](#adapter-адаптер) 35 | - [Предназначение](#предназначение-6) 36 | - [Суть паттерна](#суть-паттерна-6) 37 | - [Пример реализации](#пример-реализации-6) 38 | - [Proxy (Прокси)](#proxy-прокси) 39 | - [Предназначение](#предназначение-7) 40 | - [Суть паттерна](#суть-паттерна-7) 41 | - [Пример реализации](#пример-реализации-7) 42 | - [Composite (Композит)](#composite-композит) 43 | - [Предназначение](#предназначение-8) 44 | - [Суть паттерна](#суть-паттерна-8) 45 | - [Пример реализации](#пример-реализации-8) 46 | - [Поведенческие паттерны](#поведенческие-паттерны) 47 | - [Chain of command (цепочка команд)](#chain-of-command-цепочка-команд) 48 | - [Предназначение](#предназначение-9) 49 | - [Суть паттерна](#суть-паттерна-9) 50 | - [Пример реализации](#пример-реализации-9) 51 | - [Mediator (Посредник)](#mediator-посредник) 52 | - [Предназначение](#предназначение-10) 53 | - [Суть паттерна](#суть-паттерна-10) 54 | - [Пример реализации](#пример-реализации-10) 55 | - [Command (Команда)](#command-команда) 56 | - [Предназначение](#предназначение-11) 57 | - [Суть паттерна](#суть-паттерна-11) 58 | - [Пример реализации](#пример-реализации-11) 59 | - [State (Состояние)](#state-состояние) 60 | - [Предназначение](#предназначение-12) 61 | - [Суть паттерна](#суть-паттерна-12) 62 | - [Пример реализации](#пример-реализации-12) 63 | - [Strategy (Стратегия)](#strategy-стратегия) 64 | - [Предназначение](#предназначение-13) 65 | - [Суть паттерна](#суть-паттерна-13) 66 | - [Пример реализации](#пример-реализации-13) 67 | - [Iterator (Итератор)](#iterator-итератор) 68 | - [Предназначение](#предназначение-14) 69 | - [Суть паттерна](#суть-паттерна-14) 70 | - [Пример реализации](#пример-реализации-14) 71 | - [Template Method (Шаблонный метод)](#template-method-шаблонный-метод) 72 | - [Предназначение](#предназначение-15) 73 | - [Суть паттерна](#суть-паттерна-15) 74 | - [Пример реализации](#пример-реализации-15) 75 | - [Observer (Наблюдатель)](#observer-наблюдатель) 76 | - [Предназначение](#предназначение-16) 77 | - [Суть паттерна](#суть-паттерна-16) 78 | - [Пример реализации](#пример-реализации-16) 79 | 80 | ## Порождающие паттерны 81 | 82 | Порождающие паттерны - это шаблоны, которые описают создание объектов. 83 | 84 | ### Singleton (Одиночка) 85 | 86 | #### Предназначение 87 | Данный шаблон необходимо применять, когда необходим лишь один экземпляр класса, 88 | например для методов, которые описывают конфигурацию или создают CRUDGeneric класс. 89 | 90 | #### Суть паттерна 91 | Суть паттерна - описывать методы класса через модификаторы *public* и *static*. 92 | Таким образом с помощью модификатора *public* методы будут доступны всем, а модификатор *static*, 93 | позволяет получать доступ к методам класса без создания экземпляра класса. 94 | 95 | #### Пример реализации 96 | Паттерн используется, к примеру в фреймворке Express, где объект `app` является Singleton, с публичными методами по типу `use`. 97 | 98 | Пример кода: [Singleton](creational_patterns/Singleton.ts) 99 | 100 | ### Prototype (Прототип) 101 | 102 | #### Предназначение 103 | Данный шаблон применяется когда необходимо клонировать полностью другой класс не углубляясь в реализацию этого класса. 104 | 105 | #### Суть паттерна 106 | Создать метод внутри класса, который позволяет полностью скопировать другой класс. 107 | 108 | #### Пример реализации 109 | Глубинное клонирование классов для параллельной разработки. 110 | 111 | Пример кода: [Prototype](creational_patterns/Prototype.ts) 112 | 113 | ### Builder (Строитель) 114 | 115 | #### Предназначение 116 | Данный шаблон применяется, когда необходимо создавать большие объекты с разными конфигурациями или фильтрами. 117 | 118 | #### Суть паттерна 119 | Суть паттерна - создать большое количество простых методов, через модификатор *public*, которые можно будет последовательно 120 | через точку реализовывать на объекте, например методы фильтрации данных приходящих из базы данных. 121 | 122 | #### Пример реализации 123 | Объект позволяющий сделать объект запроса к базе данных, например агрегатор пакета mongoose. 124 | 125 | Пример кода: [Builder](creational_patterns/Builder.ts) 126 | 127 | ### Factory (Фабрика) 128 | 129 | #### Предназначение 130 | Данный шаблон применяется при создании возможности на основе базового класса создавать разные системы, которые подключаются к базовому классу через единый базовый интерфейс этого класса. 131 | 132 | #### Суть паттерна 133 | Создать базовый класс системы и описать API к подключению этой системы, после чего от этого базового класса 134 | системы наследовать все методы API для подключения и описания конкретной другой системы. 135 | 136 | #### Пример реализации 137 | Система отправки товара интернет-магазина, как базовая система со своим API, от которой могут наследоваться системы 138 | описывающее реализацию отправку Новой Почтой, Укрпочтой, или отправка курьером 139 | 140 | Пример кода: [Factory](creational_patterns/Factory.ts) 141 | 142 | ## Структурные паттерны: 143 | 144 | Структурные паттерны - это шаблоны проектирования, которые типизируют структурирования кода. 145 | 146 | ### Bridge (Мост) 147 | 148 | #### Предназначение 149 | Данный шаблон применяется, когда есть потребность реализовать одинаковые формат API, для разных сервисов, которые выполняют одну и ту же функциональность. 150 | 151 | #### Суть паттерна 152 | Создать общий интерфейс для каждого из класса сервиса, где описать все методы API. 153 | Реализовать каждый класс, такого сервиса через имплементацию этого интерфейса, после чего реализовать класс, 154 | в конструктор которого передается этот же интерфейс и в общие методы API группируются в один метод, при этом этот метод 155 | в себе ссылается на this аргумента реализующий этот интерфейс. 156 | 157 | #### Пример реализации 158 | Приложение, по отправке сообщений. При этом оно должно реализовывать функциональность мгновенных и отложенных сообщений, 159 | а так же реализовывать эту функциональность для разного типа мессенджеров, таких как Telegram, WatchApp или Viber. 160 | 161 | Пример кода: [Bridge](structure_patterns/Bridge.ts) 162 | 163 | ### Facade (Фасад) 164 | 165 | #### Предназначение 166 | Данный шаблон применяется, когда необходимо за простым API спрятать перечень сервисов или несколько 167 | бизнес-логик, которые не связаны между собой. 168 | 169 | #### Суть паттерна 170 | Создать классы сервисов, которые должны быть реализованы в ходе работы примитивного действия. 171 | Также создания класса, который реализовывает простой API интерфейс и под его же колпаком, внедряются экземпляры 172 | классов различных сервисов. Эти сервисы внедряются с модификатором *private* поскольку сервисы 173 | должны быть инкапсулированы и не должны быть допустимы к использованию вне класса. 174 | 175 | #### Пример реализации 176 | Клавиша отправки уведомления на почту, которая под капотом имеет следующий сайд эффект в формате реализации нескольких логик: 177 | - в виде логгирования (добавления реализации LoggerService). 178 | - сервис автологгирования, который автоматически подтягивает данные из регистрации, как данные для логирования в почтовом, другом, сервисе (LoginService). 179 | - реализация сервиса шаблона, который реализовывает разметку данного типа уведомления (MailService). 180 | 181 | Пример кода: [Facade](structure_patterns/Facade.ts) 182 | 183 | ### Adapter (Адаптер) 184 | 185 | #### Предназначение 186 | Данный шаблон используется, когда необходимо подсоединить два различных класса с различными типами данных. 187 | 188 | #### Суть паттерна 189 | Создать или получить класс, который имеет неподходящий тип данных для портирования (соединения с другим классом). 190 | Создать класс, который бы наследовался от этого класса, и одновременно с этим, внутри своего конструктора принимает 191 | класс, к которому нужно адаптить, после чего внутри такого класса создать метод, который бы уже реализовывал приходящие 192 | данные не адаптированого класса в нужный нам формат. 193 | 194 | #### Пример реализации 195 | Получение данных от стороннего сервиса, и которые необходимо как-то обработать, для получения необходимого формата 196 | данных, которые пользуются другим сторонним сервисом. 197 | 198 | Пример кода: [Adapter](structure_patterns/Adapter.ts) 199 | 200 | ### Proxy (Прокси) 201 | 202 | #### Предназначение 203 | Данный шаблон используется, когда необходимо с работой отдельно взятого класса, происходили сопутствующие действия (side effects). 204 | 205 | #### Суть паттерна 206 | Создать общий интерфейс, который имплементит каждый из классов: класс, который выполняет логику и класс, который 207 | проксирует свою логику поверх первого класса. Необходимо понимать, что Proxy не расширяет один класс другим, как при 208 | классическом extends, а именно что оборачивает одну логику поверх другой. 209 | 210 | #### Пример реализации 211 | Получение данных из базы данных, только если данные пользователя подходят под авторские права. 212 | Так логику доступа к базе данных описывается в просто классе, а логика проверки на соответствие пользователя в классе, 213 | который проксирует первый. 214 | 215 | Пример кода: [Proxy](structure_patterns/Proxy.ts) 216 | 217 | ### Composite (Композит) 218 | 219 | #### Предназначение 220 | Данный шаблон используется при реализации древовидных бизнес-логик и инкапсулирования их в простое API. 221 | 222 | #### Суть паттерна 223 | Создать абстрактный класс с абстрактными методами, которые наследуются разными вариантами реализации абстрактного класса, 224 | в ходе описания реализующих классов абстрактные классы перезаписываются и модифицируются модификатором *overrite*, 225 | для защиты от исчезновения методов в абстрактном классе. После чего создается результирующий класс, который 226 | реализовывает те же абстрактные методы в простое API. 227 | 228 | #### Пример реализации 229 | Интернет-магазин имеет возможность продажи как в готовой заводской упаковке - первая ветка дерева, так и cамостоятельно 230 | упаковать несколько товаров - другая ветка дерева. Необходимо реализовать метод, который обходил бы 231 | дерево и давал итоговую стоимость как в случае с упаковкой, так и без. 232 | 233 | Пример кода: [Composite](structure_patterns/Composite.ts) 234 | 235 | ## Поведенческие паттерны 236 | 237 | Поведенческие паттерны - это паттерны, которые унифицируют поведение объектов, классов или функций. 238 | 239 | ### Chain of command (цепочка команд) 240 | 241 | #### Предназначение 242 | Данный шаблон используется при реализации цепочки различных обработчиков 243 | 244 | #### Суть паттерна 245 | Создать абстрактный класс, который в себе реализовывает 2 метода: 246 | - next - метод принимающий другой обработчик. Этот, другой, обработчик передаётся по цепочке. 247 | - handle - условие. Если условие возвращает true, то данные передаются по цепочке следующему обработчику. Если нет - выход из цепочки. 248 | 249 | #### Пример реализации 250 | Middleware в работе express.js или nest.js 251 | 252 | Пример кода: [Chain of command](behavioral_patterns/Chain_of_command.ts) 253 | 254 | ### Mediator (Посредник) 255 | 256 | #### Предназначение 257 | Паттерн в основном используется на Frontend, чтобы связать компоненты, которые разрознено должны знать о друг друге. 258 | 259 | #### Суть паттерна 260 | Например, какое-то событие EventEmitter необходимо выпустить какое-то событие, при этом, это событие, когда воспроизводится, 261 | то должно производить логирование этого события, а также само событие попадать в кэш. Таким образом выходит, что каждый из этих 262 | компонентов имеет между собой какие-то связи, таким образом создавая лишние зависимости. Mediator же является посредником между 263 | всеми этими компонентами, таким образом отвязывая все зависимости между компонентами и завязывает их только на себе. 264 | 265 | #### Пример реализации 266 | Например, компонент формы 267 | 268 | Пример кода: [Mediator](behavioral_patterns/Mediator.ts) 269 | 270 | ### Command (Команда) 271 | 272 | #### Предназначение 273 | Паттерн предназначен для отделения отдельной задачи, как входной точки в модуль, в отдельную команду, через которую и 274 | управляется соединения модуля с другими модулями, а точнее управление бизнес-логики между этими модулями. 275 | 276 | #### Суть паттерна 277 | При написании приложений на Nest.js используются модули, таким образом отделяя логику по отдельным сущностям, но что если 278 | одна сущность используется в различных других модулях? Например, UserService используется непосредственно в своём контроллере, 279 | шлёт какие-то данные по WebSockets через Gateway о том, что такой-то пользователь был создан или куда-то вошел, а также 280 | дополнительно ещё идёт какая-то синхронизация очереди в базе данных. 281 | 282 | Таким образом при изменении бизнес логики самого UserService, например отложенное добавления пользователя, вынуждает 283 | менять логику каждой реализации с контроллером, веб-сокетами или базой данных. 284 | 285 | Чтобы этого избежать создаётся команда, например команда getUser и уже в рамках этой команды изменяется самая бизнес логика, 286 | поскольку эта команда является входной точкой к сущности User. 287 | 288 | #### Пример реализации 289 | Используется например как CQRS - разделения запросов чтения и записи. 290 | 291 | Пример кода: [Command](behavioral_patterns/Command.ts) 292 | 293 | ### State (Состояние) 294 | Состояние отдельного элемента. 295 | 296 | #### Предназначение 297 | Когда у объекта может быть несколько состояний. 298 | 299 | #### Суть паттерна 300 | По сути замена горы if`ов или же switch case на плавный переход к классам, которые имеют свои условия и ограничения. 301 | Получается, что вместо того, чтобы хранить всё состояние внутри нашего компонента, организовывается ссылка на текущее состояние у объекта, 302 | которая является надклассом над состоянием этого компонента (оно является абстрактным). После чего вся логика которая 303 | относится к каждому из этапов лежит лишь в классе этого этапа. 304 | 305 | #### Пример реализации 306 | Например, у нас есть статья в блог, которая может быть в трёх состояниях: 307 | - черновик, если статья закончена, она отправляется на модерацию. 308 | - на модерации, при этом если статья не проходит модерацию, то статья откатывается на черновик, иначе переходит в состояние публикации. 309 | - опубликована, если статья не понравилась, то снять её с публикации и вернуть в черновик. 310 | 311 | Пример кода: [State](behavioral_patterns/State.ts) 312 | 313 | ### Strategy (Стратегия) 314 | 315 | #### Предназначение 316 | Удаление дублирования кода, путём интегрирования его в один класс. 317 | 318 | #### Суть паттерна 319 | Выделение схожих алгоритмов поведения в общие классы, после чего использования эти алгоритмы через классы путём создания 320 | общего интерфейса для одного алгоритма и уже различные стратегии имплементируют этот интерфейс. 321 | 322 | #### Пример реализации 323 | Часто используются для авторизации, где есть наборы схожих алгоритмов, чтобы проверить авторизован ли пользователь. 324 | Но при этом может быть разные провайдеры: JWT, GitHub, GoogleAuthToken и так далее - к примеру Passport.js 325 | 326 | Пример кода: [Strategy](behavioral_patterns/Strategy.ts) 327 | 328 | ### Iterator (Итератор) 329 | 330 | #### Предназначение 331 | Когда необходимо обойти ряд объектов по какому-то принципу - по приоритету или по зависимостям. То есть паттерн используется 332 | в любом месте, где необходимо сделать какой-то обход коллекции. 333 | 334 | #### Суть паттерна 335 | Создавая общий интерфейс итератора, от которого имплементируются классы различных вариантов обхода. После этого в каждый 336 | список можно добавить этот интерфейс, через который реализовываются классы итераторов. 337 | 338 | #### Пример реализации 339 | Типы обхода различных коллекций 340 | 341 | Пример кода: [Iterator](behavioral_patterns/Iterator.ts) 342 | 343 | ### Template Method (Шаблонный метод) 344 | 345 | #### Предназначение 346 | Метод используется, когда работа определенного объекта связанного с разными объектами связана общим шаблоном действий. 347 | 348 | #### Суть паттерна 349 | Создаётся некий абстрактный класс, который в себе имеет различные методы. При этом часть этих методов может быть абстрактными 350 | и не зависеть от API, а часть может быть обычными, поскольку завязаны на самом API. После этого создаются различные классы этих API, 351 | которые специфичные каждый для своего API. 352 | 353 | #### Пример реализации 354 | Например, есть форма заявки, которая работает с несколькими API 355 | 356 | Пример кода: [Template Method](behavioral_patterns/Template_method.ts) 357 | 358 | ### Observer (Наблюдатель) 359 | 360 | #### Предназначение 361 | Создание моста из события и функции, которые реагируют на это событие и после этого делают соответствующую 362 | логику. 363 | 364 | #### Суть паттерна 365 | Есть интерфейс, который описывает схему работы непосредственно объекта генерирующего событие, а также интерфейс наблюдателя. 366 | У интерфейса наблюдателя есть метод подписаться на прослушку различных событий. Получается, чтобы получить обновление данных, не нужно к объекту 367 | ходить, он сам генерирует событие и остальные объекты могут получать уведомление о том, что событие сработало. 368 | 369 | #### Пример реализации 370 | Ситуации, где есть подписчики на событие и есть функция, которая генерирует это событие. 371 | 372 | Пример кода: [Observer](behavioral_patterns/Observer.ts) -------------------------------------------------------------------------------- /behavioral_patterns/Chain_of_command.ts: -------------------------------------------------------------------------------- 1 | interface IMiddleware { 2 | next(mid: IMiddleware): IMiddleware; 3 | handle(request: any): any; 4 | } 5 | 6 | abstract class AbstractMiddleware implements IMiddleware{ 7 | private nextMiddleware: IMiddleware 8 | 9 | next(mid: IMiddleware): IMiddleware { 10 | this.nextMiddleware = mid; 11 | return mid 12 | } 13 | 14 | handle(request: any): any { 15 | if (this.nextMiddleware) { 16 | return this.nextMiddleware.handle(request) 17 | } 18 | 19 | return 20 | } 21 | } 22 | 23 | class AuthMiddleware extends AbstractMiddleware { 24 | override handle(request: any): any { 25 | console.log('AuthMiddleware'); 26 | 27 | if (request.userId === 1) { 28 | return super.handle(request) 29 | } 30 | 31 | return { error: 'Вы не авторизованы' } 32 | } 33 | } 34 | 35 | class ValidateMiddleware extends AbstractMiddleware { 36 | override handle(request: any): any { 37 | console.log('ValidateMiddleware'); 38 | 39 | if (request.body) { 40 | return super.handle(request) 41 | } 42 | 43 | return { error: 'Нет тела запроса' } 44 | } 45 | } 46 | 47 | class Controller extends AbstractMiddleware { 48 | override handle(request: any): any { 49 | console.log('Controller') 50 | return { success: request } 51 | } 52 | } 53 | 54 | const controller = new Controller() 55 | const validate = new ValidateMiddleware() 56 | const auth = new AuthMiddleware() 57 | 58 | auth.next(validate).next(controller); 59 | 60 | console.log(auth.handle({ 61 | userId: 3 62 | })) 63 | 64 | console.log(auth.handle({ 65 | userId: 1 66 | })) 67 | 68 | console.log(auth.handle({ 69 | userId: 1, 70 | body: 'Я тело запроса' 71 | })) -------------------------------------------------------------------------------- /behavioral_patterns/Command.ts: -------------------------------------------------------------------------------- 1 | class User { 2 | constructor( public userId: number) {} 3 | } 4 | 5 | class CommandHistory { 6 | public commands: Command[] = []; 7 | push(command: Command) { 8 | this.commands.push(command) 9 | } 10 | 11 | remove(command: Command) { 12 | this.commands = this.commands.filter(c => c.commandId !== command.commandId) 13 | } 14 | } 15 | 16 | abstract class Command { 17 | public commandId: number; 18 | abstract execute(): void; 19 | 20 | constructor(public history: CommandHistory) { 21 | this.commandId = Math.random() 22 | } 23 | } 24 | 25 | class AddUserCommand extends Command { 26 | constructor( 27 | private user: User, 28 | private receiver: UserService, 29 | history: CommandHistory 30 | ) { 31 | super(history); 32 | } 33 | 34 | execute(): void { 35 | this.receiver.saveUser(this.user) 36 | this.history.push(this) 37 | } 38 | 39 | undo() { 40 | this.receiver.deleteUser(this.user.userId) 41 | this.history.remove(this) 42 | } 43 | } 44 | 45 | class UserService { 46 | saveUser(user: User) { 47 | console.log(`Сохраняя пользователя с ID ${user.userId}`) 48 | } 49 | 50 | deleteUser(userId: number) { 51 | console.log(`Удаляем пользователя с ID ${userId}`) 52 | } 53 | } 54 | 55 | class Controller { 56 | receiver: UserService; 57 | history: CommandHistory = new CommandHistory() 58 | 59 | addReceiver(receiver: UserService) { 60 | this.receiver = receiver 61 | } 62 | 63 | run() { 64 | const addUserCommand = new AddUserCommand( 65 | new User(1), 66 | this.receiver, 67 | this.history 68 | ) 69 | 70 | addUserCommand.execute(); 71 | console.log(addUserCommand.history); 72 | addUserCommand.undo(); 73 | console.log(addUserCommand.history); 74 | } 75 | } 76 | 77 | const controller =new Controller(); 78 | controller.addReceiver(new UserService()); 79 | controller.run() 80 | -------------------------------------------------------------------------------- /behavioral_patterns/Iterator.ts: -------------------------------------------------------------------------------- 1 | class Task { 2 | constructor(public priority: number) {} 3 | } 4 | 5 | class TaskList { 6 | private tasks: Task[]; 7 | 8 | public sortByPriority() { 9 | this.tasks = this.tasks.sort((a,b) => { 10 | if (a.priority > b.priority) { 11 | return 1; 12 | } else if (a.priority = b.priority) { 13 | return 0; 14 | } else { 15 | return -1; 16 | } 17 | 18 | }) 19 | } 20 | 21 | public addTask(task: Task): void { 22 | this.tasks.push(task) 23 | } 24 | 25 | public getTask (): Task[] { 26 | return this.tasks 27 | } 28 | 29 | public count (): number { 30 | return this.tasks.length 31 | } 32 | 33 | public getIterator () { 34 | return new PriorityTaskIterator(this) 35 | } 36 | } 37 | 38 | interface IIterator { 39 | current(): T | undefined; 40 | next(): T | undefined; 41 | prev(): T | undefined; 42 | index(): number; 43 | } 44 | 45 | class PriorityTaskIterator implements IIterator{ 46 | private position: number = 0; 47 | private taskList: TaskList; 48 | 49 | constructor(taskList: TaskList) { 50 | taskList.sortByPriority(); 51 | this.taskList = taskList; 52 | } 53 | 54 | current(): Task { 55 | return this.taskList.getTask()[this.position]; 56 | } 57 | next(): Task { 58 | this.position += 1; 59 | return this.taskList.getTask()[this.position]; 60 | } 61 | prev(): Task { 62 | this.position -= 1; 63 | return this.taskList.getTask()[this.position]; 64 | } 65 | index(): number { 66 | return this.position; 67 | } 68 | } 69 | 70 | const taskList = new TaskList(); 71 | taskList.addTask(new Task(8)); 72 | taskList.addTask(new Task(2)); 73 | taskList.addTask(new Task(14)); 74 | taskList.addTask(new Task(1)); 75 | 76 | const iterator = taskList.getIterator(); 77 | console.log(iterator.current()) 78 | console.log(iterator.next()) 79 | console.log(iterator.prev()) 80 | console.log(iterator.index()) 81 | 82 | 83 | -------------------------------------------------------------------------------- /behavioral_patterns/Mediator.ts: -------------------------------------------------------------------------------- 1 | interface Mediator { 2 | notify(sender: string, event: string): void; 3 | } 4 | 5 | abstract class Mediated { 6 | mediator: Mediator; 7 | setMediator(mediator: Mediator) { 8 | this.mediator = mediator 9 | } 10 | } 11 | 12 | 13 | 14 | class Notifications { 15 | send() { 16 | console.log('Отправляю уведомление') 17 | } 18 | } 19 | 20 | class Log { 21 | log(message: string) { 22 | console.log('message', message) 23 | } 24 | } 25 | 26 | class EventHandler extends Mediated { 27 | MyEvent() { 28 | this.mediator.notify('EventHandler', 'myEvent') 29 | } 30 | } 31 | 32 | class NotificationMediator implements Mediator { 33 | 34 | constructor( 35 | public notifications: Notifications, 36 | public log: Log, 37 | public eventHandler: EventHandler 38 | ) {} 39 | 40 | notify(sender: string, event: string) { 41 | switch (event) { 42 | case 'myEvent': 43 | this.notifications.send(); 44 | this.log.log('Отправлено'); 45 | break 46 | } 47 | } 48 | } 49 | 50 | const handler = new EventHandler(); 51 | const logger = new Log(); 52 | const notifications = new Notifications(); 53 | 54 | const m = new NotificationMediator( 55 | notifications, 56 | logger, 57 | handler 58 | ) 59 | 60 | handler.setMediator(m) 61 | handler.MyEvent() -------------------------------------------------------------------------------- /behavioral_patterns/Observer.ts: -------------------------------------------------------------------------------- 1 | interface Observer { 2 | update(subject: Subject): void; 3 | } 4 | 5 | interface Subject { 6 | attach(observer: Observer): void; 7 | detach(observer: Observer): void; 8 | notify(): void; 9 | } 10 | 11 | class Lead { 12 | constructor( 13 | public name: string, 14 | public phone: string 15 | ) {} 16 | } 17 | 18 | class NewLead implements Subject { 19 | private observers: Observer[] = []; 20 | public state: Lead; 21 | 22 | attach(observer: Observer): void { 23 | // @ts-ignore 24 | if (this.observers.includes(observer)) { 25 | return; 26 | } 27 | this.observers.push(observer) 28 | } 29 | 30 | detach(observer: Observer): void { 31 | const observerIndex = this.observers.indexOf(observer) 32 | if (observerIndex == -1) { 33 | return; 34 | } 35 | this.observers.splice(observerIndex, 1); 36 | } 37 | 38 | notify(): void { 39 | for (const observer of this.observers) { 40 | observer.update(this); 41 | } 42 | } 43 | } 44 | 45 | class NotificationService implements Observer { 46 | update(subject: Subject): void { 47 | console.log('NotificationService получил уведомление'); 48 | console.log(subject) 49 | } 50 | } 51 | 52 | class LeadService implements Observer { 53 | update(subject: Subject): void { 54 | console.log('LeadService получил уведомление'); 55 | console.log(subject) 56 | } 57 | } 58 | 59 | const subject = new NewLead() 60 | subject.state = new Lead('Влад', '380955551222') 61 | const s1 = new NotificationService() 62 | const s2 = new LeadService() 63 | 64 | subject.attach(s1) 65 | subject.attach(s2) 66 | subject.notify(); 67 | subject.detach(s1); 68 | subject.notify(); -------------------------------------------------------------------------------- /behavioral_patterns/State.ts: -------------------------------------------------------------------------------- 1 | class DocumentItem { 2 | public text: string; 3 | private state: DocumentItemState 4 | 5 | getState() { 6 | return this.state 7 | } 8 | 9 | setState(state: DocumentItemState) { 10 | this.state = state; 11 | this.state.setContext(this) 12 | } 13 | 14 | constructor() { 15 | this.setState(new DraftDocumentItemState()) 16 | } 17 | 18 | publishDoc() { 19 | this.state.publish() 20 | } 21 | deleteDoc() { 22 | this.state.delete() 23 | } 24 | } 25 | 26 | abstract class DocumentItemState { 27 | public name: string; 28 | public item: DocumentItem; 29 | 30 | public setContext(item: DocumentItem) { 31 | this.item = item; 32 | } 33 | 34 | public abstract publish(): void; 35 | 36 | public abstract delete(): void; 37 | } 38 | 39 | class DraftDocumentItemState extends DocumentItemState { 40 | constructor() { 41 | super(); 42 | this.name = 'DraftDocumentItemState' 43 | } 44 | 45 | public publish(): void { 46 | console.log(`На сайт отправлен текст ${this.item.text}`) 47 | this.item.setState(new PublishDocumentItemState()) 48 | } 49 | public delete(): void { 50 | console.log(`Документ удалён`) 51 | } 52 | } 53 | 54 | class PublishDocumentItemState extends DocumentItemState { 55 | constructor() { 56 | super(); 57 | this.name = 'PublishDocumentItemState' 58 | } 59 | 60 | public publish(): void { 61 | console.log(`Нельзя опубликовать опубликованый документ`) 62 | } 63 | 64 | public delete(): void { 65 | console.log(`Снято с публикации`) 66 | this.item.setState(new DraftDocumentItemState()) 67 | } 68 | } 69 | 70 | const item = new DocumentItem(); 71 | item.text = 'Первый пост' 72 | console.log(item.getState()) 73 | item.publishDoc() 74 | console.log(item.getState()) 75 | item.deleteDoc() 76 | console.log(item.getState()) -------------------------------------------------------------------------------- /behavioral_patterns/Strategy.ts: -------------------------------------------------------------------------------- 1 | class User { 2 | githubToken: string; 3 | jwtToken: string; 4 | } 5 | 6 | interface AuthStrategy { 7 | auth(user: User): boolean; 8 | } 9 | 10 | class Auth { 11 | constructor(private strategy: AuthStrategy) {} 12 | 13 | setStrategy(strategy: AuthStrategy) { 14 | this.strategy = strategy 15 | } 16 | 17 | public authUser(user: User): boolean { 18 | return this.strategy.auth(user); 19 | } 20 | } 21 | 22 | class JWTStrategy implements AuthStrategy { 23 | auth(user: User): boolean { 24 | if (user.jwtToken) { 25 | return true 26 | } 27 | return false 28 | } 29 | } 30 | 31 | class GitHubStrategy implements AuthStrategy { 32 | auth(user: User): boolean { 33 | if (user.githubToken) { 34 | return true 35 | } 36 | return false 37 | } 38 | } 39 | 40 | const user = new User(); 41 | user.jwtToken = 'token'; 42 | const auth = new Auth(new JWTStrategy()) 43 | auth.authUser(user); 44 | console.log(auth) 45 | auth.setStrategy(new GitHubStrategy()) 46 | console.log(auth) -------------------------------------------------------------------------------- /behavioral_patterns/Template_method.ts: -------------------------------------------------------------------------------- 1 | class Form { 2 | constructor(public name: string) {} 3 | } 4 | 5 | abstract class SaveForm { 6 | public save(form: Form) { 7 | const res = this.fill(form); 8 | this.log(res); 9 | this.send(res); 10 | }; 11 | 12 | protected abstract fill(form: Form): T; 13 | protected log(data: T): void { 14 | console.log(data); 15 | }; 16 | 17 | protected abstract send(data: T): void; 18 | }; 19 | 20 | class FirstAPI extends SaveForm { 21 | protected fill(form: Form): string { 22 | return form.name; 23 | }; 24 | protected send(data: string): void { 25 | console.log(`Отправляю: `, data); 26 | }; 27 | }; 28 | 29 | class SecondAPI extends SaveForm<{ fio: string }> { 30 | protected fill(form: Form): { fio: string } { 31 | return { fio: form.name }; 32 | }; 33 | protected send(data: { fio: string }): void { 34 | console.log(`Отправляю: `, data); 35 | }; 36 | }; 37 | 38 | const form1 = new FirstAPI(); 39 | form1.save(new Form('Влад')); 40 | 41 | const form2 = new SecondAPI(); 42 | form2.save(new Form('Влад')); 43 | 44 | 45 | -------------------------------------------------------------------------------- /creational_patterns/Builder.ts: -------------------------------------------------------------------------------- 1 | enum ImageFormat { 2 | Png = 'png', 3 | Jpeg = 'jpeg' 4 | } 5 | 6 | interface IResolution { 7 | width: number; 8 | height: number 9 | } 10 | 11 | interface IImageConversion extends IResolution { 12 | format: ImageFormat 13 | } 14 | 15 | class ImageBuilder { 16 | private formats: ImageFormat[] = [] 17 | private resolutions: IResolution[] = [] 18 | 19 | addPng() { 20 | if (this.formats.includes(ImageFormat.Png)) { 21 | return this 22 | } 23 | this.formats.push(ImageFormat.Png); 24 | return this 25 | } 26 | 27 | addJpeg() { 28 | if (this.formats.includes(ImageFormat.Jpeg)) { 29 | return this 30 | } 31 | this.formats.push(ImageFormat.Jpeg); 32 | return this 33 | } 34 | 35 | addResolution(width: number, height: number) { 36 | this.resolutions.push({ width, height}) 37 | return this 38 | } 39 | 40 | build(): IImageConversion[] { 41 | const res: IImageConversion[] = [] 42 | for (const r of this.resolutions) { 43 | for (const f of this.formats) { 44 | res.push({ 45 | format: f, 46 | width: r.width, 47 | height: r.height 48 | }) 49 | } 50 | } 51 | return res 52 | } 53 | } -------------------------------------------------------------------------------- /creational_patterns/Factory.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pestsov-v/Typescript-OOP-pattern/3b55d3cd00aa2135ca1e010b8000307b5c3a2e2c/creational_patterns/Factory.ts -------------------------------------------------------------------------------- /creational_patterns/Prototype.ts: -------------------------------------------------------------------------------- 1 | interface Prototype { 2 | clone(): T 3 | } 4 | 5 | class UserHistory implements Prototype { 6 | createdAt: Date; 7 | constructor(public email: string, public name: string) { 8 | this.createdAt = new Date(); 9 | } 10 | 11 | clone(): UserHistory { 12 | let target = new UserHistory(this.email, this.name); 13 | target.createdAt = this.createdAt; 14 | return target 15 | } 16 | } 17 | 18 | let user = new UserHistory('vladislav.pestsov@gmail.com', 'Vladislav') 19 | console.log(user); 20 | let user2 = user.clone() 21 | console.log(user2) -------------------------------------------------------------------------------- /creational_patterns/Singleton.ts: -------------------------------------------------------------------------------- 1 | class MyMap { 2 | private static instance: MyMap 3 | map: Map = new Map () 4 | 5 | private constructor() {} 6 | 7 | clean() { 8 | this.map = new Map() 9 | } 10 | 11 | public static get(): MyMap { 12 | if (!MyMap.instance) { 13 | MyMap.instance = new MyMap() 14 | } 15 | return MyMap.instance 16 | } 17 | } 18 | 19 | class Service1 { 20 | addMap(key: number, value: string) { 21 | const myMap = MyMap.get(); 22 | myMap.map.set(key, value) 23 | } 24 | } 25 | 26 | class Service2 { 27 | getKeys(key: number) { 28 | const myMap = MyMap.get(); 29 | console.log(myMap.map.get(key)) 30 | myMap.clean() 31 | console.log(myMap.map.get(key)) 32 | } 33 | } 34 | 35 | new Service1().addMap(1, 'Working!') 36 | new Service2().getKeys(1) -------------------------------------------------------------------------------- /structure_patterns/Adapter.ts: -------------------------------------------------------------------------------- 1 | class KVDatabase { 2 | private db: Map = new Map() 3 | save(key: string, value: string) { 4 | this.db.set(key, value) 5 | } 6 | } 7 | 8 | class PersistentDB { 9 | savePersistent(_: Object) {} 10 | } 11 | 12 | class PersistentDBAdapter extends KVDatabase { 13 | constructor(public database: PersistentDB) { 14 | super() 15 | } 16 | 17 | override save(key: string, value: string): void { 18 | this.database.savePersistent({key, value}) 19 | } 20 | } 21 | 22 | function run (base: KVDatabase) { 23 | base.save('key', 'value') 24 | } -------------------------------------------------------------------------------- /structure_patterns/Bridge.ts: -------------------------------------------------------------------------------- 1 | interface IProvider { 2 | sendMessage(messsage: string): void; 3 | connect(config: T): void; 4 | disconnect(): void; 5 | } 6 | 7 | class TelegramProvider implements IProvider { 8 | connect(config: T): void { 9 | console.log(config) 10 | } 11 | disconnect(): void { 12 | console.log(`Disconnected TG`) 13 | } 14 | sendMessage(messsage: string): void { 15 | console.log(messsage) 16 | } 17 | } 18 | 19 | class WhatAppPRovider implements IProvider { 20 | connect(config: T): void { 21 | console.log(config) 22 | } 23 | disconnect(): void { 24 | console.log(`Disconnected WA`) 25 | } 26 | sendMessage(messsage: string): void { 27 | console.log(messsage) 28 | } 29 | } 30 | 31 | class NotificationSender { 32 | constructor (private provider: IProvider) {} 33 | 34 | send() { 35 | this.provider.connect('connect') 36 | this.provider.sendMessage('message') 37 | this.provider.disconnect() 38 | } 39 | } 40 | 41 | class DelayNotificationSender extends NotificationSender { 42 | constructor(provider: IProvider) { 43 | super(provider) 44 | } 45 | 46 | sendDeployed() {} 47 | } 48 | 49 | const sender = new NotificationSender(new TelegramProvider()) 50 | sender.send(); 51 | 52 | const sender2 = new NotificationSender(new WhatAppPRovider()) 53 | sender2.send(); -------------------------------------------------------------------------------- /structure_patterns/Composite.ts: -------------------------------------------------------------------------------- 1 | abstract class DeliveryItem { 2 | items: DeliveryItem[] 3 | 4 | addItem(item: DeliveryItem) { 5 | this.items.push(item) 6 | } 7 | 8 | getItemPrices(): number { 9 | return this.items.reduce((acc: number, i: DeliveryItem) => acc += i.getItemPrices(), 0) 10 | } 11 | 12 | abstract getPrice(): number 13 | } 14 | 15 | class DeliveryShop extends DeliveryItem { 16 | constructor(private delivaryFee: number) { 17 | super() 18 | } 19 | 20 | getPrice(): number { 21 | return this.items.reduce((acc: number, i: DeliveryItem) => acc += i.getItemPrices(), 0) + this.delivaryFee 22 | } 23 | } 24 | 25 | class Package extends DeliveryItem {s 26 | getPrice(): number { 27 | return this.getItemPrices() 28 | } 29 | } 30 | 31 | class Product extends DeliveryItem { 32 | constructor(private price: number) { 33 | super() 34 | } 35 | 36 | getPrice(): number { 37 | return this.price 38 | } 39 | } 40 | 41 | const shop = new DeliveryShop(100) 42 | shop.addItem(new Product(1000)) 43 | const pack1 = new Package() 44 | pack1.addItem(new Product(200)) 45 | pack1.addItem(new Product(300)) 46 | shop.addItem(pack1) 47 | const pack2 = new Package() 48 | pack2.addItem(new Product(30)) -------------------------------------------------------------------------------- /structure_patterns/Facade.ts: -------------------------------------------------------------------------------- 1 | class Notify { 2 | send(template: string, to: string) { 3 | console.log(`Отправляю ${template}: ${to}`) 4 | } 5 | } 6 | 7 | class Log { 8 | log(message: string) { 9 | console.log(message) 10 | } 11 | } 12 | 13 | class Template { 14 | private templates = [ 15 | {name: 'other', template: `

Шаблон

`} 16 | ] 17 | 18 | getByName(name: string) { 19 | return this.templates.find(t => t.name === name) 20 | } 21 | } 22 | 23 | class NotificationFacade { 24 | private notify: Notify; 25 | private log: Log; 26 | private template: Template; 27 | 28 | constructor() { 29 | this.notify = new Notify() 30 | this.log = new Log() 31 | this.template = new Template() 32 | } 33 | 34 | send(to: string, templateName: string) { 35 | const data = this.template.getByName(templateName); 36 | if (!data) { 37 | this.log.log('Не найден шаблон') 38 | return; 39 | } 40 | 41 | this.notify.send(data.template, to) 42 | this.log.log('Шаблон отправлен') 43 | } 44 | } 45 | 46 | const s = new NotificationFacade() 47 | s.send('a@a.ua', 'other') -------------------------------------------------------------------------------- /structure_patterns/Proxy.ts: -------------------------------------------------------------------------------- 1 | interface IPaymentAPI { 2 | getPaymenyDetail(id: number): IPaymentDetail | undefined; 3 | } 4 | 5 | interface IPaymentDetail { 6 | id: number; 7 | sum: number; 8 | } 9 | 10 | class PaymentAPI implements IPaymentAPI { 11 | private data = [{id: 1, sum: 1000 }] 12 | getPaymenyDetail(id: number): IPaymentDetail | undefined { 13 | return this.data.find(d => d.id === id) 14 | } 15 | } 16 | 17 | class PaymentAccessProxy implements IPaymentAPI { 18 | constructor(private api: PaymentAPI, private userId: number) {} 19 | 20 | getPaymenyDetail(id: number): IPaymentDetail | undefined{ 21 | if (this.userId === 1) { 22 | return this.api.getPaymenyDetail(id) 23 | } 24 | console.log('Попытка получить данные платежа') 25 | return undefined 26 | } 27 | } 28 | 29 | const proxy = new PaymentAccessProxy(new PaymentAPI(), 1) 30 | console.log(proxy.getPaymenyDetail(1)) 31 | 32 | const proxy2 = new PaymentAccessProxy(new PaymentAPI(), 2) 33 | console.log(proxy.getPaymenyDetail(2)) --------------------------------------------------------------------------------