Паттерн Module — «JavaScript Design Patterns, 2017» Эдди Османи. Перевод: часть 3

Оригинальный текст — https://addyosmani.com/


Паттерн Module

Модули

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

В JavaScript существует несколько вариантов реализации модулей, а именно:

  • Паттерн Module
  • Литеральное объявление объекта
  • AMD модули
  • CommonJS модули
  • ECMAScript Harmony модули

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

Паттерн Module частично основан на объектных литералах, поэтому имеет смысл сначала поговорить о них.

Литерал объекта

Литеральное объявлении объекта описывается как набор разделенных запятыми пар ключ-значение, заключенных в фигурные скобки  {}. Имена внутри объекта могут быть либо строками, либо идентификаторами, за которыми следует двоеточие. После последней пары ключ-значение не должно быть запятой, так как это может привести к ошибке.

Объектные литералы не требуют наличия new  при создании экземпляра, при этом они не должны использоваться в начале кода, поскольку открывающая фигурная скобка {может быть истолкована как начало блока. Вне объекта могут быть добавлены новые свойства, например так — myModule.property = "someValue";

Ниже мы можем увидеть более полный пример модуля с примененим литерального объявления:

Использование объектных литералов может помочь инкапсулировать и организовать ваш код. Ребекка Мурфи ранее писала об этой теме подробнее .

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

Паттерн Module

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

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

«Приватность»

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

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

Следует отметить, что на самом деле не существует термина «приватности» внутри JavaScript, потому что в отличие от других языков он не имеет модификаторов доступа (например public или private). Переменные не могут быть объявлены публичными или приватными, поэтому мы используем область видимости функции для имитации этой концепции. В паттерне модуля объявленные переменные и методы доступны только внутри самого модуля, благодаря замыканию. Однако, переменные или методы, определенные в возвращаемом объекте, доступны всем.

История

Шаблон модуля был первоначально разработан несколькими людьми, включая Ричарда Корнфорда в 2003 году. Позднее он был популяризирован Дугласом Крокфордом в своих лекциях. Если вы когда-либо использовали библиотеку YUI Yahoo, некоторые ее функции могут показаться довольно знакомыми, и причина этого в том, что паттерн модуля сильно повлиял на YUI при создании их компонентов.

Примеры

Давайте рассмотрим реализацию паттерна модуля:

В данном примере мы не сможем напрямую обратиться к incrementCounter()или resetCounter(). Переменная счетчика (counter) также полностью защищена от нашей глобальной области видимости, поэтому она действует так же, как и приватная переменная, — ее существование ограничено внутри замыкания модуля, так что единственным способом, доступным для работы с переменной, являются две наши функции. Чтобы обратиться к методам нашего модуля, нам необходимо указать перед вызовом функции название модуля (например, «testModule»).

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

Посмотрите на другой пример: ниже мы видим корзину покупок, реализованную с использованием паттерна. Сам модуль полностью автономен. Он определен в глобальной переменной basketModule. Массив basket  является приватным и поэтому другие части нашего приложения (из глобальной области) не могут непосредственно воспользоваться им. Корзина существует только в замыкании модуля и поэтому единственные способом получить доступ к нему это наши методы (т.е. addItem()getItemCount() и т.д.).

Запомните, что из модуля мы возвращаем объект. Это необходимо, чтобы мы могли использовать basketModule в глобальной области видимости:

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

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

Использование

Импорт библиотек

Этот пример шаблона демонстрирует, как глобальные переменные (например, jQuery, Underscore) могут быть переданы в качестве аргументов анонимной функции нашего модуля. Это эффективно позволяет нам импортировать их и локально использовать их по своему усмотрению.

Экспорт

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

Использование во фреймворках

Dojo

Dojo 1.7

ExtJS

YUI

jQuery

Преимущества паттерна Module

Ранее мы увидели, почему паттерн конструктора является полезеным, но в чем преимущества паттерна модуля перед ним? Во-первых, для разработчиков, привыкших к классическому ООП, идея инкапсуляции намного ближе. Во-вторых, он поддерживает приватные данные, к которым нельзя обратиться из внешнего мира.

Недостатки паттерна Module

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

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

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

Для дальнейшего ознакомления с паттерном модуля можете прочитать подробную статью Бена Черри.