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

Автор: Валерий Съестов Дата публикации: 04.11.2013
Последнее обновление: 03.04.2014

Одним из наиболее важных аспектов написания эффективного и поддерживаемого кода, есть возможность замечать повторяющиеся участки этого кода и оптимизировать их. Это та область, где знание шаблонов проектирования может оказаться бесценным.

Что такое шаблон проектирования?

Шаблон представляет собой многократно решение, которое может быть применено к часто встречающейся проблемы в разработке программного обеспечения - в нашем случае - в написании веб-приложений на JavaScript. С другой стороны, шаблон проектирования выступает в качестве шаблона, который может быть использован для решения ряда различных задач в различных ситуациях.

И так, почему важно знать и понимать шаблоны проектирования? Шаблоны проектирования имеют три основных преимущества:

  1. Шаблоны представляют собой проверенные решения. Они обеспечивают твердые подходы к решению вопросов в разработке программного обеспечения, используя проверенные методы, которые отражают опыт и знания разработчиков, которые помогли определить их и довести до шаблона.
  2. Шаблоны могут быть легко использованы повторно: обычно отражает модель из коробки решения, которые могут быть адаптированы к нашим собственным потребностям. Эта особенность делает их весьма надежными.
  3. Шаблоны могут быть выразительным: Когда мы смотрим на шаблон в целом, то присутствует определенная структура, и готовые пути решения, с помощь которых можно представить решение сложной задачи простым способом.

Шаблоны представляют собой не точное решение. Важно, что бы мы понимали роль шаблона, задачей которого является предоставление нам схемы решения. Шаблоны не решают всех проблем проектирования и не заменяют хорошие проектирование программного обеспечения, однако такие возможности они поддерживают. Так же шаблоны проектирования обладают еще следующими преимуществами:

  • Повторное использование шаблонов способствует предотвращению незначительных проблем, которые могут вызвать более серьезные, в процессе разработки приложений. Это означает, что когда код основан на проверенной модели, мы можем позволить себе тратить меньше времени, на организацию структуры кода и больше времени уделять качеству самого приложения. Это потому, что шаблоны дают возможность держать код в более структурированном и организованном состоянии и избежать необходимости реорганизовывать его для удобства сопровождения в будущем.
  • Шаблоны могут предоставлять обобщенные решения, которые не обязательно могут быть привязаны к конкретной проблеме. Этот обобщенный подход означает, что независимо от приложения (и во многих случаях языка программирования) мы работаем с шаблонами проектирования, которые могут быть применены для улучшения структуры нашего кода.
  • Некоторые модели могут реально уменьшить общий размер файла нашего кода, избегая повторений. Стимулируя разработчика более внимательно исследовать участки кода разрабатываемого приложения, для выявления повторов, создания универсальных методов, которые могут выполняться повторно в других частях приложения, тем самым сокращая общим объем программы.
  • Шаблоны проектирования добавлены в словарь разработчиков, что дает возможность более ясно выражать свои мысли в команде при работе над крупными приложениями.
  • Шаблоны, которые часто используются могут быть улучшены с течением времени путем использования коллективного опыта других разработчиков, использующих эту модели вносить свой вклад в сообщество шаблонов проектирования. В некоторых случаях это приводит к созданию совершенно новых шаблонов проектирования или же привести к усовершенствованию существующего. Это может гарантировать, что на основе шаблонов приложение становится все более надежными, чем могло бы быть без их использования.

Мы уже используем шаблоны в повседневной жизни

Чтобы понять, какую пользу дают шаблоны, давайте рассмотрим очень простой пример, проблему выбора DOM элементов, которую библиотека JQuery решает за нас.

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

  1. Выбрать все элементы на странице, и путем ручного перебора проверять каждый элемент, соответствует ли его класс тому, который мы ищем.
  2. Использовать современную встроенную функцию браузера querySelectorAll() для выбора всех элементов с классом “Foo”.
  3. Использование встроенной функции, такой как getElementsByClassName() и получить нужную коллекцию.

Итак, какой из этих вариантов является самым быстрым? Это на самом деле вариант под номером 3, С коэффициентом в 8-10 раз. Но в реальных приложениях, 3-й вариант может и не работать, так как версии Internet Explorer ниже 9 поддерживают его не полностью и в таком случае необходимо использовать другие способы .

Разработчики, которые используют библиотеки, такие как JQuery, не беспокоятся о подобных проблемах, так как функции выбора элементов на странице учитывают подобные нюансы связанные с различными браузерами и их возможностями.

Категории шаблонов проектирования

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

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

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

Некоторые из шаблонов которые попадают в эту категорию:

  • Абстрактная фабрика (Abstract Factory).
  • Прототип (Prototype).
  • Одиночка (Singleton).
  • Строитель (Builder).

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

Это шаблоны проектирования, в котором рассматривается вопрос о том, как из классов и объектов образуются более крупные структуры.

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

Некоторые из шаблонов которые попадают в эту категорию:

  • Декоратор (Decorator).
  • Фасад (Facade).
  • Приспособленец (Flyweight).
  • Адаптер (Adapter).
  • Заместитель (Proxy).

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

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

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

Некоторые из шаблонов которые попадают в эту категорию:

  • Итератор (Iterator).
  • Посредник (Mediator).
  • Наблюдатель (Observer).
  • Посетитель (Vistor).

Общая таблица категорий шаблонов проектирования

Ниже представлена таблица, со списком шаблонов проектирования, которая выступает в качестве памятки, с описанием и назначением каждого из шаблонов.

Creational (Порождающие) На основе концепции создания объекта
Class
Factory Method (Фабричный метод) Создает экземпляры нескольких производных классов на основе данных интерфейса или событий
Object
Abstract Factory (Абстрактная фабрика) Предоставляет интерфейс для создания семейств, связанных между собой, или независимых объектов, конкретные классы которых неизвестны
Builder (Строитель) Отделяет конструирование сложного объекта от его представления, позволяя использовать один и тот же процесс конструирования для создания различных представлений
Prototype (Прототип) Описывает виды создаваемых объектов с помощью прототипа и создает новые объекты путем его копирования
Singleton (Одиночка) Гарантирует, что некоторый класс может иметь только один экземпляр, и предоставляет
Structural (Структурные) Базируется на построении блоков из объектов
Class
Adapter (Адаптер) Преобразует интерфейс класса в некоторый другой интерфейс, ожидаемый клиентами. Обеспечивает совместную работу классов, которая была бы невозможна без данного шаблона из-за несовместимости интерфейсов
Object
Adapter (Адаптер) Преобразует интерфейс класса в некоторый другой интерфейс, ожидаемый клиентами. Обеспечивает совместную работу классов, которая была бы невозможна без данного шаблона из-за несовместимости интерфейсов
Bridge (Мост) Отделяет абстракцию от реализации, благодаря чему появляется возможность независимо изменять и то и другое
Composite (Компоновщик) Группирует объекты в древовидные структуры для представления иерархий типа “часть-целое”. Позволяет клиентам работать с единичным объектами так же, как с группами объектов
Decorator (Декоратор) Динамически возлагает на объект новые функции. Декораторы применяются для расширения имеющейся функциональности и являются гибкой альтернативой порождения подклассов
Facade (Фасад) Представляет унифицированный интерфейс к множеству интерфейсов в некоторой подсистеме. Определяет интерфейс более высокого уровня, облегчающий работу с подсистемой
Flyweight (Приспособленец) Использует разедление для эффективной поддержки большого числа мелких объектов
Proxy (Заместитель) Подменяет другой объект для контроля доступа к нему
Behavioral (Поведенческие) На основе концепции создания объекта
Class
Interpreter (Интерпретатор) Для заданного языка определяется представление его грамматики, а также интерпретатор предложений языка, использующий это представление
Template Method (Шаблонный метод) Определяет скелет алгоритма, перекладывая ответственность за некоторые его шаги на подклассы. Позволяет подклассам переопределять шаги алгоритма, не меняя его общей структуры
Object
Chain of Responsibility (Цепочка обязаностей) Можно избежать жесткой зависимости отправителя запроса от его получателя, при этом запросом начинает обрабатываться один из несколько объектов. Объекты получатели связываются в цепочку, и запрос передается по цепочке, пока какой-то объект его не обработает
Command (Команда) Инкапсулирует запрос в виде объекта, позволяя тем самым параметризировать клиентов типом запроса, устанавливать очередность запросов, протоколировать их и поддерживать отмену выполнения операций
Iterator (Итератор) Дает возможность последовательно обойти все элементы составного объекта, не расскрывая его внутреннего представления
Mediator (Посредник) Определяет объект, в котором инкапсулировано знание о том, как взаимодействуют объекты из некоторого множества. Способствует уменьшению числа связей между объектами, позволяя им работать без явных ссылок друг на друга. Это, в свою очередь, дает возможность независимо изменять схему взаимодействия
Memento (Хранитель) Позволяет, не нарушая инкапсуляции, получить и сохранить во внешней памяти внутренее состояние объекта, чтобы позже объект можно было восстановить точно в таком же состоянии
Observer (Наблюдатель) Определяет между объектами зависимость типа один - ко - многим, так что при изменении состоянии одного объекта все зависящие от него получают извещение и автоматически обновляются
State (Состояние) Позволяет объекту варьировать свое поведение при изменении внутреннего состояния. При этом создается впечатление, что поменялся класс объекта
Strategy (Стратегия) Определяет семейство алгоритмов, инкапсулируя их все и позволяя подставлять один вместо другого. Можно менять алгоритм независимо от клиента, который им пользуется
Visitor (Посетитель) Представляет операцию, которую надо выполнять над элементами объекта. Позволяет определить новую операцию, не меняя классы элементов, к которым он применяется