#5 Singleton паттерн в Javascript.

poster
В этом уроке мы разберем что такое синглтон паттерн в Javascript и как его написать.
Понравилось? Поделитесь с друзьями!
Понравилось?
Поделитесь с друзьями!
Комментарии
Текст видео

Всем привет. В этом уроке мы с вами разберем, что такое pattern singleton и как его писать в javascript. Вообще что такое singleton? Это подход, когда класс может иметь только один экземпляр и есть какая-то точка доступа к этому экземпляру. Так как в javascript нет классов, то это можно перефразировать, как обьект может иметь только один экземпляр и есть какая-то точка доступа к этому экземпляру.

Давайте попробуем. Мы напишем модуль, который будет хранить в себе счетчик и иметь 2 публичных метода getCounter и increaseCounter.

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

var counterModule = (function () {
  var counter = 0;

  var getCounter = function () {
    return counter;
  }

  var increaseCounter = function () {
    counter ++;
  }
})();

Внутри напишем переменную counter и методы getCounter, который будет возвращать наш counter. И метод increaseCounter, который будет увеличивать наш счетчик.

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

return {
  getInstance: function () {
    return instance || (instance = createInstance());
  }
}

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

var counterModule = (function () {
  var instance,
      counter = 0;

  var getCounter = function () {
    return counter;
  }

  var increaseCounter = function () {
    counter ++;
  }

  var createInstance = function () {
    return {
      getCounter: getCounter,
      increaseCounter: increaseCounter
    }
  }

  return {
    getInstance: function () {
      return instance || (instance = createInstance());
    }
  }
})();

Добавим переменную instance и еще один метод createInstance, который будет возвращать 2 публичных метода: getCounter и increaseCounter.

Еще раз, чтобы было понятно.

  1. У нас есть модуль counterModule
  2. Внутри него у нас есть 2 переменных counter и instance.
  3. Каждый раз, когда мы будем вызывать increaseCounter, у нас будет увеличиваться наш счетчик
  4. getCounter мы используем для того, чтобы получить значение нашего counter сейчас
  5. Функция createInstance у нас вызывается в самый первый раз, пока instance еще не существует
  6. Если же instance у нас уже есть, то возвращается этот же instance.

Давайте попробуем. Если мы в консоли напишем counterModule.getInstance(), то мы видим обьект с двумя методами getCounter и increaseCounter. И сколько мы раз getInstance мы не вызывали, то все равно он ссылается на один и тот же instance.

Теперь мы можем сделать

counterModule.getInstance().getCounter()
counterModule.getInstance().increaseCounter()
counterModule.getInstance().getCounter()

По умолчанию нам вывелся счетчик 0. После того, как мы вызвали increaseCounter, он изменился на 1. Как мы видим, если мы вызовем getCounter опять, то ничего не поменялось. Это у нас singleton и мы не перезатерли экземпляр нашего обьекта и все работает правильно.

Вот самый простой пример singleton в javascript. Впринципе желательно знать паттерны javascript и как работает singleton и применять это когда вам необходимо.

Только зарегистрированные пользователи могут оставлять комментарии.  Войдите, пожалуйста.
Ияза Гара
7 лет назад
Я извиняюсь, но какой смысл в паразитном методе getInstance()? Что мешает просто вернуть два метода? return { getCounter: getCounter, increaseCounter: increaseCounter }
monsterlessons
7 лет назад
Ничего не мешает. Но тогда это будет просто обьект, а не синглтон. Опять же у вас может быть 50 методов и это не всегда удобно.
Ияза Гара
7 лет назад
Не соглашусь! Если выбросить getInstance(), то класс полностью сохранит своё поведение. Вот пример: https://jsfiddle.net/9oa3pfyu/ Собственно, в примере - псевдо-синглтон. Поэтому смысла в getInstance() в данном случае не вижу.
vlad
8 лет назад
исходный код как-то не очень отрабатывает. при console.log(counterModule.getInstance()) выдает { getCounter: [Function: getCounter], increaseCounter: [Function: increaseCounter] } альтернативный вариант отрабатывающий нормально: var mySingleton = (function(){ var instance; function privateMethod(){ return 'privateMethod' } var privateProperty = 5; function init(){ return {x:Math.random()}; } return { getInstance: function () { if ( !instance ) { instance = init(); } return instance.x; } } })(); console.log(mySingleton.getInstance()); выдает рандомное число - 0.7092041579820472
monsterlessons
8 лет назад
Все отрабатывает правильно, так как синглтон возвращает обьект с методами, где counter находится в замыкании. getInstance должен вам выдавать инстанс класса, а не рандомное число.
vlad
8 лет назад
уточнение. я скопировал исходный код в js файл и запустил (работая на винде) его через node в Git Bash. console.log(counterModule.getInstance()) результат: { getCounter: [Function: getCounter], increaseCounter: [Function: increaseCounter] } ОК console.log(counterModule.getInstance().getCounter()) console.log(counterModule.getInstance().increaseCounter()) console.log(counterModule.getInstance().getCounter()) результат: 0 undefined 1 ????????????????????????????????????????????????????? --------------------------------------------------------------------------------------------------- вариант который я предложил, в обоих случаях ВИДНА ОДНА И ТА ЖЕ ЦИФРА при Math.random. console.log(mySingleton.getInstance()) console.log(mySingleton.getInstance()) console.log(mySingleton.getInstance()) результат хоть c return instance { x: 0.21378836851423144 } { x: 0.21378836851423144 } { x: 0.21378836851423144 } ОК хоть c return instance.x 0.6496688117305751 0.6496688117305751 0.6496688117305751 ОК PS ПО ОДНОЗНАЧНОСТИ ВОСПРИЯТИЯ КОДА, вариант с mySingleton читается проще
vlad
8 лет назад
т.е. выдается один и тот же объект
monsterlessons
8 лет назад
В первом вызове getCounter выдает 0, так как это дефолтное значение счетчика. console.log(counterModule.getInstance().getCounter()) При вызове increaseCounter увеличивает значение counter и ничего не возвращает, поэтому undefined. console.log(counterModule.getInstance().increaseCounter()) При повторном вызове мы получаем 1, так как счетчик был увеличен console.log(counterModule.getInstance().getCounter()) Если вам ваш вариант читается проще, то используйте его. Урок был создан, для того чтобы люди разобрались что такое синглтон. А как его написать, вообще без разницы.
vlad
8 лет назад
console.log(counterModule.getInstance().getCounter()) console.log(counterModule.getInstance().increaseCounter()) console.log(counterModule.getInstance().getCounter()) console.log(counterModule.getInstance().increaseCounter()) console.log(counterModule.getInstance().getCounter()) console.log(counterModule.getInstance().increaseCounter()) console.log(counterModule.getInstance().getCounter()) console.log(counterModule.getInstance().increaseCounter()) console.log(counterModule.getInstance().getCounter()) console.log(counterModule.getInstance().increaseCounter()) console.log(counterModule.getInstance().getCounter()) console.log(counterModule.getInstance().increaseCounter()) console.log(counterModule.getInstance().getCounter()) РЕЗУЛЬТАТ !!!!! 0 undefined 1 undefined 2 undefined 3 undefined 4 undefined 5 undefined 6 В исходном варианте counter увеличивается БЕЗ ОСТАНОВКИ!!! В отличие от того варианта который я предложил
vlad
8 лет назад
я так понимаю при синглтоне counter не должен увеличиваться ...... или нет?
monsterlessons
8 лет назад
Естественно, что он должен увеличиваться. Singleton создается на обьект, а не на переменную в замыкании.
vlad
8 лет назад
это понятно что создается на объект. я только решил что СОДЕРЖИМОЕ ОБЪЕКТА (экземпляра) не должно изменяться. ошибся?
Sergey Illarionov
9 лет назад
Вариант в классовом выполнении в ES6: class SingletoneCounter { static instance = null; constructor() { if (SingletoneCounter.instance) { return SingletoneCounter.instance; } SingletoneCounter.instance = this; this.counter = 0; } getCounter() { return this.counter; } increaseCounter() { this.counter++; } } const singletone = new SingletoneCounter(); const singletone2 = new SingletoneCounter(); console.log(singletone.getCounter()); // 0 singletone.increaseCounter(); console.log(singletone.getCounter()); // 1 console.log(singletone2.getCounter()); // 1 console.log(singletone === singletone2); // true