Паттерн «Модуль» #1

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

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

  1. Паттерн «модуль»
  2. Литеральная объектная нотация
  3. Модули AMD
  4. Модули CommonJS
  5. Модули ECMAScript Harmony

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

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

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

var myObjectLiteral = {
     variableKey: variableValue,
     functionKey: function () {
     // ...
     }
 }; 

Литералы объекта не требуют создания  оператора new , но не следует их использовать в начале выражения, так как открытая фигурная скобка » { «может быть интерпретирована как начало блока. За пределами объекта к нему  могут быть добавлены новые члены  с помощью присваивания, делается это следующим образом: myModule.property = «someValue»;

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


var myModule = {
    myProperty: "someValue",
    // Литералы объектов могут содержать свойства и методы
     // например, мы можем определить дополнительный объект для конфигурирования модуля :
     myConfig: {
         useCaching: true,
         language: "en"
     },
    // очень простой метод
     saySomething: function () {
         console.log( "Where in the world is Paul Irish today?" );
     },
    // результат значения на основе текущей конфигурации
     reportMyConfig: function () {
         console.log( "Caching is: " + ( this.myConfig.useCaching ? "enabled" : "disabled")     );
     },
    // переопределение текущий конфигурации
     updateMyConfig: function( newConfig ) {
        if ( typeof newConfig === "object" ) {
             this.myConfig = newConfig;
             console.log( this.myConfig.language );
         }
     }
 };
// Результат : Где в мире Paul Irish сегодня?
 myModule.saySomething();
// Результат : Кэширование : включено
 myModule.reportMyConfig();
// Результат : фр
 myModule.updateMyConfig({
     language: "fr",
     useCaching: false
 });
// Результат : Кэширование : отключено
 myModule.reportMyConfig();

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

Тем не менее, если мы выбираем этот метод, мы можем быть в равной степени заинтересованы в паттерне «Модуль». Он по-прежнему использует литералы объектов, но только в качестве возвращаемого значения из функции.

Паттерн «Модуль»

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

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

Приватность

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

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

Следует отметить, что на самом деле нет истинного смысла  «приватности »  в JavaScript , потому что в отличие от некоторых традиционных языков он не имеет модификаторов доступа. Переменные не могут технически быть объявлены как публичные или  же частные, поэтому мы используем область видимости для имитации этого понятия. Благодаря замыканию в паттерне «Модуль»   переменные или методы доступны только внутри самого модуля. Однако, переменные или методы, объявленные внутри объекта, будут доступны всем.

Историческая справка

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

Примеры

Давайте посмотрим на реализацию паттерна «Модуль»   путем создания модуля, который является самодостаточным.

var testModule = (function () {
    var counter = 0;
    return {
         incrementCounter: function () {
         return counter++;
     },
    resetCounter: function () {
         console.log( "counter value prior to reset: " + counter );
         counter = 0;
     }
  };
})();
// Использование:
// Увеличиваем наш счетчик
testModule.incrementCounter();
// Проверяем значение счетчика и сбрасываем
// Результат: значение счетчика до сброса : 1
testModule.resetCounter();

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

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


var myNamespace = (function () {
    var myPrivateVar, myPrivateMethod;
    // Частная переменная счетчика
     myPrivateVar = 0;
    // Частная функция, которая выводит в консоль любые аргументы 
     myPrivateMethod = function( foo ) {
         console.log( foo );
     };
    return {
         // Общедоступная переменная
         myPublicVar: "foo",
        // Общедоступная функция
         myPublicFunction: function( bar ) {
            // Увеличиваем наш частный счетчик
             myPrivateVar++;
            // Вызываем наш частный метод используя bar
             myPrivateMethod( bar );
        }
     };
 })();

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

var basketModule = (function () {
    // privates 
    var basket = [];
    function doSomethingPrivate() {
         //...
     }
    function doSomethingElsePrivate() {
         //...
     }
    // Возвращает объект с публичными методами
     return {
        // Добавление элементов в нашу корзину
         addItem: function( values ) {
             basket.push(values);
         },
        // Получить количество элементов в корзине
         getItemCount: function () {
             return basket.length;
         },
         doSomething: doSomethingPrivate,
        // Получить общее значение предметов в корзине
         getTotal: function () {
             var q = this.getItemCount(),
             p = 0;
            while (q--) {
                 p += basket[q].price;
             }
            return p;
         }
     };
 })();

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


// basketModule возвращает объект с общедоступным API
basketModule.addItem({
     item: "bread",
     price: 0.5
 });
basketModule.addItem({
     item: "butter",
     price: 0.3
 });
//Результат: 2
 console.log( basketModule.getItemCount() );
// Результат: 0.8
 console.log( basketModule.getTotal() );
//Однако, следующее работать не будет:
// Результат: undefined
// Это происходит потому, что сама корзина не exposed как часть нашего
// публичного API
 console.log( basketModule.basket );
// Это также не будет работать , так как она существует только в пределах нашего
// basketModule замыкания, но не в возвращенном публичного объекта
 console.log( basket );

Методы выше эффективны в пространстве имен (namespace) внутри basketModul .

Продолжение статьи: Паттерн «Модуль» #2

Источник: addyosmani.com

Оцените статью
Добавить комментарий