Паттерн «Одиночка» (Singleton) известен тем, что он ограничивает создание экземпляра класса для одного объекта. В классическом стиле, паттерн «Одиночка» может быть реализован созданием класса с методом, который создает новый экземпляр класса, если его не существует. В случае, если экземпляр уже существует, он просто возвращает ссылку на этот объект.
Паттерн «Одиночка» отличается от статических классов (или объектов) тем, что мы можем задержать их инициализацию, потому что они требуют информацию, которая может быть недоступна во время инициализации.
В JavaScript «Одиночки» служат в качестве общего пространства имен ресурсов, которые изолируют код от глобального пространства имен (namespace) таким образом, чтобы обеспечить единую точку доступа для функции.
Мы можем реализовать «Одиночку» следующим образом:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
var mySingleton = (function () {
// Переменная Instance хранит ссылку на Синглтон
var instance;
function init() {
// Singleton
// Приватные методы и переменные
function privateMethod(){
console.log( «I am private» );
}
var privateVariable = «Im also private»;
var privateRandomNumber = Math.random();
return {
// Публичные методы и переменные
publicMethod: function () {
console.log( «The public can see me!» );
},
publicProperty: «I am also public»,
getRandomNumber: function() {
return privateRandomNumber;
<sp��r������r����������������������&��������������?����x�r�������������r�����@�������r������������ass=»crayon-h»> }</sp��r������r����������������������&��������������?����x�r�������������r�����@�������r������������ass=»crayon-h»>
};
};
return {
// Получим экземпляр одиночки (Singleton instance), если таковой существует
// или создадим один, если не сущетствует
getInstance: function () {
if ( !instance ) {
instance = init();
}
return instance;
}
};
})();
var myBadSingleton = (function () {
// Переменная экземпляра (Instance) хранит ссылку на одиночк
var instance;
function init() {
// Одиночка (Singleton)
var privateRandomNumber = Math.random();
return {
getRandomNumber: function() {
return privateRandomNumber;
}
};
};
return {
// Всегда создаем новый экземпляр Одиночки
getInstance: function () {
instance = init();
return instance;
}
};
})();
// Применение:
var singleA = mySingleton.getInstance();
var singleB = mySingleton.getInstance();
console.log( singleA.getRandomNumber() === singleB.getRandomNumber() ); // true
var badSingleA = myBadSingleton.getInstance();
var badSingleB = myBadSingleton.getInstance();
console.log( badSingleA.getRandomNumber() !== badSingleB.getRandomNumber() ); // true
// Примечание: так как мы работаем со случайными числами, есть </code><code>математическая вероятность, что оба числа будут такими же, </code><code>однако маловероятно.
// Иначе, приведенный выше пример должен быть по-прежнему </code><code>в силе.
|
Что позволяет «Одиночке» иметь глобальный доступ к экземпляру (как правило, через MySingleton.getInstance()) в то время как мы (по крайней мере, в статических языках) вызываем new MySingleton() напрямую. Однако, это возможно в JavaScript.
В книге GoF применение шаблона «Одиночка»описывается следующим образом:
- Там должен быть ровно один экземпляр класса, и он должен быть доступен клиентам с известной точки доступа.
- Когда единственный экземпляр должен быть расширяемым путем создания подклассов, и клиентам нужно иметь возможность использовать расширенный экземпляр без модификации своего кода.
Второй из этих пунктов относится к случаю, где нам может понадобиться такой код:
1
2
3
4
5
6
7
8
9
10
|
mySingleton.getInstance = function(){
if ( this._instance == null ) {
if ( isFoo() ) {
this._instance = new FooSingleton();
} else {
this._instance = new BasicSingleton();
}
}
return this._instance;
};
|
Здесь getInstance становится немного похожим на метод Factory, и нам не нужно обновлять постоянно код. FooSingleton выше в коде будет подклассом BasicSingleton и будет реализовывать тот же интерфейс.
Почему отложенное выполнение считается важным для «Одиночки»?
В C++ он служит для изоляции от непредсказуемого порядка динамической инициализации, возвращая контроль программисту.
Важно отметить разницу между статическим экземпляром класса (объекта) и «Одиночкой»в то время как последний может быть реализован как статический экземпляр, его можно создать «лениво», без ресурсов и памяти до тех пор, пока это не будет на самом деле необходимо.
Если у нас есть статический объект, который может быть инициализирован сразу же, нам нужно убедиться, что код всегда выполняется в том же порядке (напр., в случае objCar нужен objWheel во время его инициализации) и это не при наличии большого количества исходных файлов.
И «Одиночки» и статические объекты могут быть полезны, но ими не стоит злоупотреблять — точно также как и другими шаблонами.
На практике паттерн «Одиночка» полезен, когда ровно один объект необходим для координации других в системе. Вот пример использования паттерна в этом контексте:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
var SingletonTester = (function () {
// параметры: Объект, содержащий параметры конфигурации для одиночки (singleton)
// напр., var options = { name: «test», pointX: 5};
function Singleton( options ) {
// установить параметры для options
options = options || {};
// установить некоторые свойства для нашего singleton
this.name = «SingletonTester»;
this.pointX = options.pointX || 6;
this.pointY = options.pointY || 10;
}
// наш держатель экземпляра(instance)
var instance;
// эмуляция статических переменных и методов
var _static = {
name: «SingletonTester»,
// Метод получения экземпляра. Он возвращает
// экземпляр одиночки (Singleton instance) объекта singleton
getInstance: function( options ) {
if( instance === undefined ) {
instance = new Singleton( options );
}
return instance;
}
};
return _static;
})();
var singletonTest = SingletonTester.getInstance({
pointX: 5
});
// Выводим результат pointX just чтобы убедиться, что все верно
// Результат: 5
console.log( singletonTest.pointX );
|
Источник: addyosmani.com