Singleton и с чем его едят.
Запись от drnet_ua размещена 20.12.2010 в 14:08
Теория:
Последнее время на форуме часто спрашивают, что это такое, как и зачем.
Мэтры показывают пальцем на MCV и фыркают на синглтоны, а тем временем это очень удобная штука.
Спросим у вики, что она думает по этому поводу:
Цитата:
... the singleton pattern is a design pattern used to implement the mathematical concept of a singleton, by restricting the instantiation of a class to one object.
Одним из подвидов данного типа классов является UtilityClass, основным отличием которого
является наличие кучи независимых функций, например Math, т.е. просто набор функций зависящих только от аргумента.
Вспомним, как просто с ними работать – Math.random(). Где угодно, когда угодно… мне нравится, а вам?
А что если это будет что-то более функциональное?
Самыми интересными для нас свойствами синглтона являются:
1) гарантированно единственный экземпляр
2) возможность доступа к нему отовсюду, откуда нам хочется
Подготовимся:
Создадим самый обычный класс, с некоторой функциональностью:
package { import flash.events.*; import flash.utils.*; /** * ... * @author drnet_ua */ public class SimpleClass extends EventDispatcher { private var currentNumber:Number; private var _lastPrime:Number; private var progressTimer:Timer; public function get lastPrime():Number { return _lastPrime; } public static function isPrime(a:Number):Boolean { var multiplier:Number = 2; var numberIsPrime:Boolean = true; while (numberIsPrime && ( multiplier <= (a / multiplier))) { if ((a % multiplier) == 0) { numberIsPrime = false } else { multiplier++; } } return numberIsPrime; } private function onTimerHandler(e:TimerEvent):void { currentNumber++; while (!isPrime(currentNumber)) { currentNumber++; } _lastPrime = currentNumber; this.dispatchEvent(new Event('NEW_PRIME')); } public function StartCalculations(startNumber:Number = 1):void { currentNumber = startNumber; progressTimer = new Timer(1000, 0); progressTimer.addEventListener(TimerEvent.TIMER, onTimerHandler); progressTimer.start(); } public function SimpleClass() { } } }
После создания класса, посредствам вызова функции StartCalculations() , начиная со стартового числа startNumber каждую секунду экземпляр класса, находит следующее простое число и генерирует событие “NEW_PRIME”.
Используя это событие, мы можем получить это простое число.
Тест:
private function traceActivity(e:Event):void { trace(e.target.lastPrime); } ... var a:SimpleClass = new SimpleClass(); a.addEventListener('NEW_PRIME', traceActivity); a.StartCalculations(1);
Цитата:
[Starting debug session with FDB]
[SWF] F:\singleton\bin\singleton.swf - 2 761 bytes after decompression.
2
3
5
7
11
13
17
19
[SWF] F:\singleton\bin\singleton.swf - 2 761 bytes after decompression.
2
3
5
7
11
13
17
19
Ограничение на создание экземпляров
Так как мы не будем явно создавать экземпляр нашего класса, то всё доступное из вне, должно быть статик функциями и статик полями. Несмотря ни на что, один экземпляр все же должен существовать, пишем:
Введем флаг, запрещающий создание нашего класса без нашего разрешения:
перепишем конструктор:
public function SimpleSinglton() { if (!allowConstructor) { throw(new Error('Class cannot be instansiated')); } }

Для доступа к единственному экземпляру нашего класса(_instance) напишем статический геттер, он же будет разрешать создание класса для нас, и запрещать всем остальным:
private static get instance():SimpleSinglton { if (_instance == null) { allowConstructor = true; _instance = new SimpleSinglton(); allowConstructor = false; } return _instance; }
Если нас еще не создавали – разрешим это сделать. И на будущее – запретим.

Таким образом у нас будет только один экземпляр, и создаст он сам себя при первом вызове функции instance.
А зачем нам эта функция?? Для доступа к функционалу класса!
Так как доступа к НЕ СТАТИЧЕСКИМ методам класса у нас нет, создадим статические функции-прокси:
Для красоты переименуем нашу не статичную функцию StartCalculations в _StartCalculations, и напишем такое:
public static function StartCalculations(startNumber:Number):void { instance._StartCalculations(startNumber); }
public static function addPrimeListener(targetFunction:Function):void { instance.addEventListener('NEW_PRIME', targetFunction); } public static function removePrimeListener(targetFunction:Function):void { instance.removeEventListener('NEW_PRIME', targetFunction); }
Как этим теперь пользоваться?
Цитата:
[SWF] F:\singleton\bin\singleton.swf - 3 589 bytes after decompression.
11
13
17
19
11
13
17
19
полный листинг того что у нас получилось:
package { import flash.events.*; import flash.utils.*; /** * ... * @author drnet_ua */ public class SimpleSinglton extends EventDispatcher { private static var _instance:SimpleSinglton; private static var allowConstructor:Boolean; private var currentNumber:Number; private var _lastPrime:Number; private var progressTimer:Timer; private static function get instance():SimpleSinglton { if (_instance == null) { allowConstructor = true; _instance = new SimpleSinglton(); } return _instance; } public function get lastPrime():Number { return _lastPrime; } public static function isPrime(a:Number):Boolean { var multiplier:Number = 2; var numberIsPrime:Boolean = true; while (numberIsPrime && ( multiplier <= (a / multiplier))) { if ((a % multiplier) == 0) { numberIsPrime = false } else { multiplier++; } } return numberIsPrime; } private function onTimerHandler(e:TimerEvent):void { currentNumber++; while (!isPrime(currentNumber)) { currentNumber++; } _lastPrime = currentNumber; this.dispatchEvent(new Event('NEW_PRIME')); } public function _StartCalculations(startNumber:Number):void { currentNumber = startNumber; progressTimer = new Timer(1000, 0); progressTimer.addEventListener(TimerEvent.TIMER, onTimerHandler); progressTimer.start(); } public static function StartCalculations(startNumber:Number):void { instance._StartCalculations(startNumber); } public static function addPrimeListener(targetFunction:Function):void { instance.addEventListener('NEW_PRIME', targetFunction); } public static function removePrimeListener(targetFunction:Function):void { instance.removeEventListener('NEW_PRIME', targetFunction); } public function SimpleSinglton() { if (!allowConstructor) { throw(new Error('Class cannot be instansiated')); } } } }
Всего комментариев 37
Комментарии
![]() ![]() |
|
Цитата:
синглтон, это такой класс, который ограничивает количество своих экземпляров одним.
Одним из подвидов данного типа классов является UtilityClass, основным отличием которого является наличие кучи независимых функций, например Math И по поводу статических addEventLstener - я бы сделал статический диспатчер и вешал листенеры ему, а не экземпляру синглтона. |
|
Обновил(-а) gloomyBrain 20.12.2010 в 14:57
|
![]() ![]() |
|
Math насколько я понимаю это Utility Class
несколько попутался в терминологии ![]() нельзя получить ссылку на Math, т.к. у него нет конструктора мне важен результат в итоге - я имею доступ к классу, который что-то делает сам по себе, а не просто набору функций. откуда угодно, когда угодно и тд. в случае с тицианом - туда уходит работа с его СмартФоксом. |
|
Обновил(-а) drnet_ua 20.12.2010 в 15:16
|
![]() ![]() |
|
Здесь тема SingletonsAreEvil раскрыта более полно.
А Вики верно упоминает о единственном назначении шаблона Одиночки: существовать объект должен в единственном экземпляре. Все остальное - неверное толкование этого паттерна. |
![]() ![]() |
|
"Глобальная точка управления" - это как большая красная кнопка в штаб квартире. Маленький разгром в квартире - и вся страна под угрозой.
|
![]() ![]() |
|
Тут у каждого свое мнение
![]() Еще ни в одном хливаре не видел веских доводов "за" и "против". Где то читал что сложно тестируется такой код, но я до TDD еще не дорос. ![]() |
![]() ![]() |
|
Никогда не видел ничего плохого в синглтоне.
|
![]() ![]() |
|
GBee, а почему нельзя (или можно?) хранить эти данные в стат классе? Какие плюсы-минусы и польза именно от синглтона?
|
![]() ![]() |
|
Хм, такие классы как модель, фабрика звуков, другие фабрики - у меня все синглтоны, так логичней. Я считаю для многих вещей хороша жёсткая привязка.
|
![]() ![]() |
|
2GBee Кто тебе сказал, что на класс нельзя ссылку сделать?
И кто тебя заставляет давать своим статическим классам длинные имена. + вот это вызвать легче? Не пишите синглтонами. На крайний случай напишите статический класс с именем "O" и напихайте туда ссылки на обычные классы, и примите на веру, что они единственные. |
|
Обновил(-а) Rzer 21.12.2010 в 20:44
|
![]() ![]() |
|
Цитата:
а почему нельзя (или можно?) хранить эти данные в стат классе? Какие плюсы-минусы и польза именно от синглтона?
|
![]() ![]() |
|
Чем меньше классы знают друг о друге - тем лучше. Есть возможность их использовать повторно в других приложениях и проектах. Если в проекте есть синглтон о нем как правило знают очень много классов. У приложения появляется высокая связанность, что нехорошо. Синглтоны нужны, но использовать их нужно разумно.
Пример с Math.random() уберите чем быстрее тем лучше, к паттерну синглтон он никакого отношения не имеет. Не путайте читателей. |
|
Обновил(-а) Diestro 22.12.2010 в 11:31
|
![]() ![]() |
|
Да никто не путает, там надо смотреть сообщение целиком, вкупе с предыдущим.
|
![]() ![]() |
|
LOS2008: А синглтон какое отношение имеет к тому, что вы сейчас сказали? Смысл синглтона в том, что запрещается создание множества однотипных элементов, а не в том, что создается всего один.
Где это бывает нужно, например, так это во многопоточном окружении, когда в базе могут быть конкурирующие транзакции, и нужно, чтобы они встали в очередь, если они пытаются добраться до одних и тех же данных. Это тоже можно разрулить по-другому, но там хотя бы понятно зачем. А т.как многопоточности в АС3 нет как данности, то и необходимости в таких решениях быть не может. |
|
Обновил(-а) wvxvw 04.01.2011 в 22:16
|
![]() ![]() |
|
А как же классы инициализаторы вроде StylesManager для UIComponent?
|
Последние записи от drnet_ua
- Singleton и с чем его едят. (20.12.2010)
- Вихрь Мерсенна (Mersenne twister) — генератор псевдослучайных чисел (13.10.2010)