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

Для начала ознакомьтесь с Паттерн «Модуль» #1.

Вариации шаблона «Модуль»

Import миксины

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

// Глобальный модуль
 var myModule = (function ( jQ, _ ) {
    function privateMethod1(){
         jQ(".container").html("test");
     }
    function privateMethod2(){
         console.log( _.min([10, 5, 100, 2, 1000]) );
  }
    return{
         publicMethod: function(){
             privateMethod1();
         }
     };
// Pull in jQuery и Underscore
 })( jQuery, _ );
myModule.publicMethod();

Экспорт

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

// Глобальный модуль
 var myModule = (function () {
    // Module object
     var module = {},
         privateVariable = "Hello World";
    function privateMethod() {
         // ...
     }
    module.publicProperty = "Foobar";
    module.publicMethod = function () {
         console.log( privateVariable );
     };
    return module;
})();

Реализация «модуля» в различных библиотеках и фреймворках.

Dojo

Dojo это удобный метод работы с объектами под названием dojo.setObject(). Он  принимает в качестве первого аргумента строку, разделенную точками, такую  как myObj.parent.child, которая ссылается на свойство под названием  «child»  в пределах  объекта «parent»  , определенного  внутри «MyObj » . Использование  setObject() позволяет установить значение «детей«,  создавая любого из промежуточных объектов в остальной части пути, если таковые не существуют.

Например, если мы хотим объявить basket.core  как объект namespace store, то это может быть достигнуто следующим образом:

var store = window.store || {};
if ( !store["basket"] ) {
    store.basket = {};
}
if ( !store.basket["core"] ) {
    store.basket.core = {};
}
store.basket.core = {
    // ...остальная логика
};

Или следующим образом с помощью  Dojo 1.7 ( AMD — совместимая версия) и выше :

require(["dojo/_base/customStore"], function( store ){
    // используем dojo.setObject()
     store.setObject( "basket.core", (function() {
        var basket = [];
        function privateMethod() {
             console.log(basket);
         }
        return {
             publicMethod: function(){
                     privateMethod();
             }
         };
    })());
});

Для получения дополнительной информации  dojo.setObject()  смотрите официальную документацию.

ExtJS

Для тех, кто использует  Sencha’s ExtJS,  смотрите пример ниже, он показывает, как правильно использовать паттерн «модуль» с фреймворком.

В примере ниже  мы видим как определить пространство имен (namespace), которое затем может быть заполнено модулем, содержащим частный и публичный API.  За исключением некоторых семантических различий, это похоже на то, как паттерн «Модуль» реализуется в чистом (vanilla) JavaScript :

// создаем namespace
 Ext.namespace("myNameSpace");
// создаем приложение
 myNameSpace.app = function () {
    // отсюда не имеется доступ к DOM ; элементы еще не существуют
     // приватные переменные
     var btn1,
         privVar1 = 11;
    // приватные функции
     var btn1Handler = function ( button, event ) {
         console.log( "privVar1=" + privVar1 );
         console.log( "this.btn1Text=" + this.btn1Text );
     };
    // публичное space
     return {
         // публичные свойства,напр,. строки для перевода
         btn1Text: "Button 1",
        // публичные методы
         init: function () {
            if ( Ext.Ext2 ) {
                btn1 = new Ext.Button({
                     renderTo: "btn1-ct",
                     text: this.btn1Text,
                     handler: btn1Handler
                 });
            } else {
               btn1 = new Ext.Button( "btn1-ct", {
                    text: this.btn1Text,
                    handler: btn1Handler
                });
            }
         }
     };
 }();

YUI

Кроме того, мы также  можем реализовать паттерн «Модуль»  при создании приложений с использованием YUI3 . Следующий  пример основывается на паттерне «Модуля» во фреймворке YUI, реализованном  Эриком Миргалия, но опять же, не сильно отличающимся  от версии vanilla  JavaScript:

Y.namespace( "store.basket" ) ;
 Y.store.basket = (function () {
    var myPrivateVar, myPrivateMethod;
    // приватные переменные:
     myPrivateVar = "I can be accessed only within Y.store.basket.";
    // приватный метод:
     myPrivateMethod = function () {
         Y.log( "I can be accessed only from within YAHOO.store.basket" );
     }
    return {
         myPublicProperty: "I'm a public property.",
        myPublicMethod: function () {
             Y.log( "I'm a public method." );
            // Внутри корзины я могу получить доступ к приавтаным переменным и методам:
             Y.log( myPrivateVar );
             Y.log( myPrivateMethod() );
            // The native scope of myPublicMethod сохранен
            // поэтому у нас есть доступ к this:
             Y.log( this.myPublicProperty );
         }
     };
})();

jQuery

Существует множество способов  представить jQuery код в виде паттерна «модуль», даже если этот код не напоминает привычные jQuery плагины. Бен Черри ранее предлагал способ, при котором, если у модулей имеются  общие черты, то они объявляются через функцию-обертку.
В следующем примере  функция library  используется для объявления  новой библиотеки и при создании новой библиотеки (т.е. модуля) автоматически связывает функцию library  с  document.ready.

Y.namespace( "store.basket" ) ;
 Y.store.basket = (function () {
    var myPrivateVar, myPrivateMethod;
    // приватные переменные:
     myPrivateVar = "I can be accessed only within Y.store.basket.";
    // приватный метод:
     myPrivateMethod = function () {
         Y.log( "I can be accessed only from within YAHOO.store.basket" );
     }
    return {
         myPublicProperty: "I'm a public property.",
        myPublicMethod: function () {
             Y.log( "I'm a public method." );
            // Внутри корзины я могу получить доступ к приавтаным переменным и методам:
             Y.log( myPrivateVar );
             Y.log( myPrivateMethod() );
            // The native scope of myPublicMethod сохранен
            // поэтому у нас есть доступ к this:
             Y.log( this.myPublicProperty );
         }
     };
})();

Преимущества

Чем же хорош шаблон «Модуль»? Во-первых, он понятен разработчикам, переходящих с объектно-ориентированного программирования.

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

Недостатки

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

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

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

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

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