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

Паттерн Singleton известен тем, что он ограничивает класс одним экземпляром. Классический способ использования паттерна Singleton это создание класса с помощью метода инициализации. Метод создаст новый экземпляр класса только, если до этого он не существовал. Если экземпляр уже существует, он просто возвращает ссылку на объект.

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

Оригинальный отрывок из книги:

Singletons differ from static classes (or objects) as we can delay their initialization, generally because they require some information that may not be available during initialization time. They don’t provide a way for code that is unaware of a previous reference to them to easily retrieve them. This is because it is neither the object or «class» that’s returned by a Singleton, it’s a structure. Think of how closured variables aren’t actually closures — the function scope that provides the closure is the closure.

 

В JavaScript Singleton’ы являются пространством имён для общих ресурсов, которые изолированы от глобального пространства имен.

Мы можем реализовать Singleton следующим образом:

Обычно для получения доступа к экземпляру мы используем метод  MySingleton.getInstance(). Мы не можем вызвать  new MySingleton()напрямую. Хотя в JavaScript это возможно.

В книге GoF применимость паттерна Singleton описывается следующим образом:

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

Второй пункт относится к случаю, когда нам может понадобиться код:

Здесь getInstance немного напоминает метод Factory. FooSingleton расширяет подкласс, а BasicSingleton реализовывает базовый интерфейс.

Вопрос: почему отсрочка инициалиции считается важной для Singleton?

Ответ: в C ++ он позволяет выйти за пределы динамической инициализации, и дать управление программисту.

Важно запомнить разницу между статическим экземпляром класса (объекта) и Singleton: синглтон можно реализовывать как в виде статического экземпляра, так и лениво — с задержкой (без необходимости в ресурсах и памяти), пока в этом не появится необходимость.

Если у нас есть статический объект, который можно инициализировать напрямую, нам нужно убедиться, что код всегда выполняется в нужном порядке (например, objCar‘у требуется objWheelво время его инициализации), что достаточно трудно при развёртывании больших объектов.

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

На практике паттерн Singleton полезен, когда необходим один объект для координации других по всей системе. Вот один пример:

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

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

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