Шаблоны проектирования в JavaScript простыми словами

3
23070
Добавить в избранное

Реализуем популярные шаблоны проектирования в JavaScript и параллельно разбираемся в этой непростой теме на реальных примерах.

Шаблоны проектирования в JavaScript простыми словами

Шаблоны проектирования в JavaScript имеют ряд особенностей. В языке нет интерфейсов, поэтому приходится прибегать к некоторым договоренностям. Будем считать, что класс реализует определенный интерфейс, если он имеет присущие ему методы и свойства. Сами интерфейсы описаны в комментариях.

В примерах используется синтаксис ES6.

Реальные шаблоны проектирования в JavaScript также описаны в нашей статье Паттерны JavaScript: курс, который упростит разработку.

Порождающие шаблоны проектирования в JavaScript

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

Простая фабрика

Самостоятельно делать двери при строительстве дома было бы довольно сложно, поэтому вы получаете их готовыми из магазина.

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

Пример реализации

Создадим неявный интерфейс для всех дверей:

Организуем фабрику, которая будет их производить:

Все, можно работать:

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

Фабричный метод

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

Паттерн Фабричный метод позволяет делегировать логику создания нужных экземпляров дочерним классам.

Пример реализации

Создадим несколько типов интервьюеров с одним интерфейсом:

А вот и менеджер по подбору персонала:

Дочерние классы расширяют его и предоставляют нужных интервьюеров:

Теперь можно проводить собеседования:

Паттерн полезен, если логика работы подклассов одинаковая, но конкретный подкласс выясняется только во время выполнения программы.

Абстрактная фабрика

Вернемся к дверям. Строго говоря, деревянные двери следует покупать в магазине деревянных дверей, железные – в магазине железных, а двери из ПВХ – в специализированном ПВХ-магазине. Также требуется эксперт по установке: плотник, сварщик или специальный установщик дверей из ПВХ.

Абстрактная фабрика – это интерфейс, который группирует другие фабрики, логически связанные друг с другом.

Пример реализации

У нас есть несколько дверей:

и узкие специалисты-установщики:

Нужно все сгруппировать. Деревянные двери – с плотником, железные – со сварщиком.

Можно и дверьми заняться:

Теперь в комплекте с каждой дверью идет нужный мастер.

Паттерн полезен, когда есть несколько классов, зависящих друг от друга.

Строитель

Вы заказали себе гамбургер в ресторане быстрого питания. Сначала кассир предлагает вам выбрать хлеб, потом котлету, сыр, соусы… Логика создания гамбургера оказалась довольно сложной, тут не обойтись без паттерна Строитель.

Этот шаблон позволяет создавать различные варианты объекта без загрязнения конструктора лишним кодом.

Пример реализации

Начнем с самого гамбургера:

А вот и Строитель:

Вуаля! Вот наш бургер:

Паттерн Строитель нужен, если объект может существовать в разных вариациях или процесс инстанцирования состоит из нескольких шагов.

Синглтон

У страны должен быть единственный президент, иначе начнется беспорядок.

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

Пример реализации

В JavaScript Синглтоны могут быть реализованы с помощью шаблона Модуль. Приватные переменные и функции при этом прячутся в замыкании.

В этом примере частная информация скрыта от внешнего кода. Публичный метод позволяет узнать имя президента (но не изменить его):

Структурные шаблоны проектирования в JavaScript

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

Адаптер

Адаптером является, например, карт-ридер, который необходим, чтобы перенести на компьютер фотографии с карты памяти.

Этот паттерн оборачивает несовместимый с чем-то объект и делает его совместимым, не изменяя исходный код.

Пример реализации

Возьмем игру, в которой герой охотится на львов.

Все львы имеют один интерфейс:

Игрок может охотиться только на объекты, соответствующие этому интерфейсу:

Нужно ввести в игру дикую собаку, но у нее другой интерфейс. Чтобы совместить собаку и охотника, нужен Адаптер:

Теперь можно вводить в игру нового персонажа:

Мост

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

Паттерн Мост позволяет отделить реализацию от самого объекта и выстроить иерархию реализаций.

Пример реализации

Вот иерархия страниц вашего веб-сайта:

И отдельная иерархия различных тем оформления:

А вот их взаимодействие:

Компоновщик

Любая организация состоит из людей. Некоторые работники объединяются в группы, и сама организация – это большая группа.

Шаблон Компоновщик позволяет единообразно обрабатывать отдельные объекты и их группы. Он работает с иерархией «часть-целое».

Пример реализации

У нас есть разные типы работников:

и сама организация:

Теперь можно посчитать общую зарплату всех сотрудников:

Декоратор

В автомастерской предлагают несколько видов услуг. Чтобы рассчитать общий счет, нужно взять цену за одну услугу и последовательно прибавить все остальные, пока не получится окончательная стоимость. Каждая услуга является Декоратором.

Этот паттерн оборачивает объект и динамически изменяет его поведение.

Пример реализации

Возьмем для примера кофе. Самый простой кофе, реализующий соответствующий интерфейс:

Мы хотим иметь возможность добавлять в кофе различные добавки, для этого создадим некоторые декораторы:

Теперь вы можете сделать кофе на свой вкус:

Фасад

Чтобы включить компьютер достаточно нажать кнопку. Это очень просто, но внутри включающегося компьютера происходит много сложных действий. Простой интерфейс к сложной системе – это и есть Фасад.

Пример реализации

Создадим класс компьютера:

и простой Фасад для его сложных функций:

Так работать с компьютером намного проще:

Приспособленец

В поездах дальнего следования воду для горячих напитков кипятят в больших емкостях – сразу для всех. Это позволяет экономить электричество (или газ).

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

Пример реализации

У нас есть разные типы чая и большой чайник:

Теперь создадим чайный магазинчик, который будет принимать заказы:

Работает он следующим образом:

Прокси (Заместитель)

Представьте себе дверь с кодовым замком. Ее основная функциональность – открыться и пропустить вас в другое помещение, но сверху добавлен Прокси, который проверяет право доступа.

Заместитель обеспечивает какую-то дополнительную логику: ограничивает доступ к основному объекту, ведет логи или выполняет кэширование.

Пример реализации

Прежде всего создадим интерфейс двери и его реализацию:

А теперь напишем прокси-класс, чтобы обеспечить безопасность нашей двери:

Теперь кто попало не сможет зайти:

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

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

Цепочка обязанностей

У вас есть три счета (A, B, C) с разными суммами и разным приоритетом использования. Сначала проверяется A, если на нем достаточно денег для покупки, то цепочка прерывается. Иначе проверяется B, затем C. Цепь будет продолжаться, пока не найдет подходящий обработчик.

Паттерн Цепочка обязанностей позволяет выстроить объекты в такую цепь.

Пример реализации

Вот ваша учетная запись, которая реализует логику связи счетов:

Теперь составим цепочку:

Команда

Вы приходите в ресторан и делает заказ официанту. Он перенаправляет вашу Команду шеф-повару, который знает, что и как готовить.

Паттерн Команда инкапсулирует некоторые действия и необходимые для них данные и позволяет отделить Клиента от Получателя.

Пример реализации

Это Получатель, который умеет совершать различные действия:

А вот набор команд:

Это код Вызывающего (официанта из примера):

А вот пример использования:

Итератор

У музыкальных плееров есть кнопки next и previous, которые позволяют последовательно перебирать песни или радиостанции.

Паттерн Итератор предоставляет доступ к элементам объекта, не раскрывая способ их внутреннего представления.

Пример реализации

Радиостанция:

Итератор:

Можно добавлять и удалять станции, а также получать их частоту:

Посредник

Когда вы разговариваете с кем-нибудь по мобильному телефону, то между вами есть Посредник – провайдер.

В этом паттерне объект Посредника управляет взаимодействием между двумя другими объектами (Коллегами), уменьшая связь между ними.

Пример реализации

Создадим простой чат, в котором пользователи (Коллеги) могут отправлять сообщения друг другу.

Чат выглядит так:

А это сами пользователи:

Начнем беседу:

Хранитель

Некоторые калькуляторы умеют сохранять в памяти последнее выражение, то есть последнее состояние вычислений.

Шаблон Хранитель захватывает текущее состояние объекта и дает возможность восстанавливать его.

Пример реализации

Создадим текстовый редактор с функцией сохранения контента.

Объект Хранителя:

Сам редактор:

Теперь смело можете писать курсовую, все данные сохранятся!

Наблюдатель

Этот паттерн также известен как шаблон публикации-подписки.

На сайтах поиска работы вы можете подписаться на интересные вам параметры вакансий. Когда подходящее предложение появляется, сайт отправляет вам уведомление.

Паттерн Наблюдатель позволяет оповещать всех заинтересованных объектов о произошедших изменениях.

Пример реализации

Соискатели хотят получать уведомления:

А Доска объявлений может эти уведомления рассылать:

Подпишемся на рассылку:

Посетитель

Чтобы отправиться за границу, нужно получить разрешение (визу). Но оказавшись в стране, вы можете спокойно посещать самые разные места, не спрашивая дополнительного разрешения. Достаточно лишь узнать о них.

Паттерн Посетитель позволяет добавлять объектам дополнительные операции, не изменяя их исходный код.

Пример реализации

Смоделируем зоопарк с разными видами животных:

Теперь мы хотим послушать, какие звуки они издают. Для этого создадим Посетителя:

Он просто обращается к каждому классу и вызывает нужный метод:

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

Вуаля:

Стратегия

Для упорядочивания некоторого набора данных вы используете алгоритм пузырьковой сортировки. Она отлично справляется с небольшими объемами, но тормозит с крупными. У быстрой сортировки противоположная проблема. Тогда вы решаете изменять алгоритм в зависимости от размера набора. Это ваша Стратегия.

Шаблон Стратегия позволяет переключать используемый алгоритм в зависимости от ситуации.

Пример реализации

Воплотить Стратегию в JavaScript помогут функции первого класса.

А это клиент, который может использовать любую стратегию:

Теперь можно сортировать массивы:

Состояние

Вы рисуете в Paint. В зависимости от вашего выбора кисть меняет свое состояние: рисует красным, синим или любым другим цветом.

Паттерн Состояние позволяет изменять поведение класса при изменении состояния.

Пример реализации

Создадим текстовый редактор, в котором можно менять состояние текста – жирный, курсив и т. д.

Это функции преобразования:

А вот и сам редактор:

Можно работать:

Шаблонный метод

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

Шаблонный метод определяет «скелет» алгоритма, но делегирует реализацию шагов дочерним классам.

Пример реализации

Создадим инструмент для тестирования, сборки и разворачивания приложения.

Базовый класс определяет скелет алгоритма сборки:

А дочерние классы – конкретную реализацию каждого шага:

Соберем проект:

Перевод статьи Design Patterns for Humans! JavaScript Edition

Хотите получать больше интересных материалов с доставкой?

Подпишитесь на нашу рассылку:

И не беспокойтесь, мы тоже не любим спам. Отписаться можно в любое время.




Комментариев: 3

  1. Vyacheslav Goriachikh

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

  2. В оригинале статьи (и у вас, соответственно) неправильная реализация паттерна «Итератор», интерфейс паттерна предполагает наличие методов first, next, isDone (или hasNext), currentItem. Тут их нет.
    Вот правильная реализация: https://www.dofactory.com/javascript/iterator-design-pattern.
    Некоторое количество паттернов (очень мало число) раскрыто не лучшим образом. Но статья хороша полнотой покрытия.

  3. Потрясающе, давно искал что-то похожее. Спасибо за перевод!

Добавить комментарий