Статьи

Шаблон Singleton в JavaScript: не требуется

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

Объект на объектно-ориентированном языке в основном играет одну из двух ролей: это либо структура данных, либо компонент, предоставляющий услугу. В первом случае вы склонны делать несколько копий (например, узлы дерева). В последнем случае часто требуется только одна копия (подумайте, менеджер базы данных). Паттерн синглтона (в математике синглтон — это набор с ровно одним элементом) был изобретен для языков классов, чтобы помочь им реализовать компоненты. В таких языках вы не можете напрямую создать объект компонента, вам нужен класс для этого (есть исключения, но давайте игнорируем их ради этого поста). С одноплодной шаблон, вы делаете вид , что класс является  его единственный экземпляр и создать непосредственную связь между классом и экземпляром. Ниже приведена реализация шаблона синглтона в Java [1]:

    public class Singleton {
 
        public static final Singleton INSTANCE = new Singleton();
 
        // Private constructor prevents external instantiation
        private Singleton() {
        }
        
        public void amethod() {
            // ...
        }
    }

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

Так как насчет JavaScript? Он позволяет вам напрямую создавать объекты (являясь одним из немногих языков программирования, которые позволяют вам это делать), и в этом нет необходимости в хитрости.

    var namespace = {
        singleton: {
            amethod: function() {
                console.log("amethod");
            }
        }
    };
    // Invoke: namespace.singleton.amethod()

Пространство имен не является строго необходимым, но приятное прикосновение. Все становится немного сложнее, если вы хотите создать синглтон по запросу (лениво).

    var namespace = {
        _singleton: null,
        getSingleton: function() {
            if (!this._singleton) {
                this._singleton = {
                    amethod: function() {
                        console.log("amethod");
                    }
                }
            }
            return this._singleton;
        }
    };
    // Invoke: namespace.getSingleton().amethod()

Если вы можете позволить себе работать только на более новых движках JavaScript, вы можете использовать getter [2], чтобы сделать вышеупомянутое решение более привлекательным.

    var namespace = {
        _singleton: null,
        get singleton() {
            if (!this._singleton) {
                this._singleton = {
                    amethod: function() {
                        console.log("amethod");
                    }
                }
            }
            return this._singleton;
        }
    };
    // Invoke: namespace.singleton.amethod()

Соображения безопасности

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

  • Предотвращение создания копий: программисты, переходящие с Java на JavaScript, кажутся более обеспокоенными этим, чем другие. Вы делаете это, полностью скрывая конструктор экземпляра. Консультируйтесь с [3] для углубленного трактата.
  • Предотвратите обмен синглтона: злоумышленник может попытаться обменять ваш синглтон на их реализацию. Сделав свойство  singleton  в пространстве имен доступным только для чтения и неконфигурируемым (т. Е. Параметр только для чтения нельзя изменить), вы можете предотвратить это.
        Object.defineProperty(namespace, "singleton",
            { writable: false, configurable: false, value: { ... } });
    
  • Предотвращение изменений в синглтоне: методыObject.preventExtensions (),  Object.seal () иObject.freeze ()  предоставляют вам (все более тщательно) средства для блокировки объекта [4].
  • Сохраняйте конфиденциальность данных: это опять-таки кажется наиболее важным для людей Java. Смотрите решение ниже.

Оборачивая немедленно вызванное выражение функции (IIFE, [5]) вокруг синглета, вы можете скрыть переменную, содержащую синглтон.

    var namespace = {
        getSingleton: (function() { // BEGIN iife
            var singleton;
            return function() {
                if (!singleton) {
                    singleton = {
                        amethod: function() {
                            console.log("amethod");
                        }
                    }
                }
                return singleton;
            };
        }()) // END iife
    };
    // Invoke: namespace.getSingleton().amethod()

Связанное чтение:

  1. Шаблон Singleton и его простейшая реализация в Java
  2. JavaScript Getters и Setters
  3. Шаблон синглтон дизайна в JavaScript
  4. Функция Object.freeze (JavaScript)
  5. Область видимости переменной JavaScript и ее подводные камни

 

С http://www.2ality.com/2011/04/singleton-pattern-in-javascript-not.html