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
Комментарии
20.12.2010 14:55 | |
Цитата:
синглтон, это такой класс, который ограничивает количество своих экземпляров одним.
Одним из подвидов данного типа классов является UtilityClass, основным отличием которого является наличие кучи независимых функций, например Math И по поводу статических addEventLstener - я бы сделал статический диспатчер и вешал листенеры ему, а не экземпляру синглтона. |
|
Обновил(-а) gloomyBrain 20.12.2010 в 14:57
|
20.12.2010 15:03 | |
Math насколько я понимаю это Utility Class
несколько попутался в терминологии нельзя получить ссылку на Math, т.к. у него нет конструктора мне важен результат в итоге - я имею доступ к классу, который что-то делает сам по себе, а не просто набору функций. откуда угодно, когда угодно и тд. в случае с тицианом - туда уходит работа с его СмартФоксом. |
|
Обновил(-а) drnet_ua 20.12.2010 в 15:16
|
20.12.2010 15:38 | |
Здесь тема SingletonsAreEvil раскрыта более полно.
А Вики верно упоминает о единственном назначении шаблона Одиночки: существовать объект должен в единственном экземпляре. Все остальное - неверное толкование этого паттерна. |
20.12.2010 16:07 | |
"Глобальная точка управления" - это как большая красная кнопка в штаб квартире. Маленький разгром в квартире - и вся страна под угрозой.
|
21.12.2010 01:29 | |
Тут у каждого свое мнение
Еще ни в одном хливаре не видел веских доводов "за" и "против". Где то читал что сложно тестируется такой код, но я до TDD еще не дорос. |
21.12.2010 01:31 | |
Никогда не видел ничего плохого в синглтоне.
|
21.12.2010 15:08 | |
GBee, а почему нельзя (или можно?) хранить эти данные в стат классе? Какие плюсы-минусы и польза именно от синглтона?
|
21.12.2010 17:02 | |
Хм, такие классы как модель, фабрика звуков, другие фабрики - у меня все синглтоны, так логичней. Я считаю для многих вещей хороша жёсткая привязка.
|
21.12.2010 20:22 | |
2GBee Кто тебе сказал, что на класс нельзя ссылку сделать?
И кто тебя заставляет давать своим статическим классам длинные имена. + вот это вызвать легче? Не пишите синглтонами. На крайний случай напишите статический класс с именем "O" и напихайте туда ссылки на обычные классы, и примите на веру, что они единственные. |
|
Обновил(-а) Rzer 21.12.2010 в 20:44
|
21.12.2010 21:13 | |
Цитата:
а почему нельзя (или можно?) хранить эти данные в стат классе? Какие плюсы-минусы и польза именно от синглтона?
|
22.12.2010 11:29 | |
Чем меньше классы знают друг о друге - тем лучше. Есть возможность их использовать повторно в других приложениях и проектах. Если в проекте есть синглтон о нем как правило знают очень много классов. У приложения появляется высокая связанность, что нехорошо. Синглтоны нужны, но использовать их нужно разумно.
Пример с Math.random() уберите чем быстрее тем лучше, к паттерну синглтон он никакого отношения не имеет. Не путайте читателей. |
|
Обновил(-а) Diestro 22.12.2010 в 11:31
|
22.12.2010 12:20 | |
Да никто не путает, там надо смотреть сообщение целиком, вкупе с предыдущим.
|
04.01.2011 22:10 | |
LOS2008: А синглтон какое отношение имеет к тому, что вы сейчас сказали? Смысл синглтона в том, что запрещается создание множества однотипных элементов, а не в том, что создается всего один.
Где это бывает нужно, например, так это во многопоточном окружении, когда в базе могут быть конкурирующие транзакции, и нужно, чтобы они встали в очередь, если они пытаются добраться до одних и тех же данных. Это тоже можно разрулить по-другому, но там хотя бы понятно зачем. А т.как многопоточности в АС3 нет как данности, то и необходимости в таких решениях быть не может. |
|
Обновил(-а) wvxvw 04.01.2011 в 22:16
|
04.01.2011 23:31 | |
А как же классы инициализаторы вроде StylesManager для UIComponent?
|
Последние записи от drnet_ua
- Singleton и с чем его едят. (20.12.2010)
- Вихрь Мерсенна (Mersenne twister) — генератор псевдослучайных чисел (13.10.2010)