
Паттерн Singleton
А давайте расскажу интересное — про паттерн проектирования Синглтон. Кто-то знает и пользуется, а кому-то вдруг и пригодится.

Согласно одному из определений, паттерн проектирования — это пример наиболее удачного проектного решения в области объектно-ориентированного программирования, описание взаимодействия объектов и классов для решения стандартной задачи проектирования. Другими словами — это наиболее удачный подход к построению структуры классов и взаимосвязи между ними для решения некой стандартной задачи в проектировании программы.
Синглтон («Singleton» — одиночка) используется в случае, если на все приложение необходим только один экземпляр определенного класса, доступ к которому можно будет иметь из любого места.
Предположим, что мы имеем некий класс SoundManager, который отвечает за воспроизведение всех звуков в игре. Скажем, есть у него публичный метод playExplosionSound(). До того, как мне стало известно о синглтоне, для того, чтобы вызвать этот метод из какого-нибудь глубоко упрятанного класса, приходилось делать что-то вроде myParent.myParent.soundManager.playExplosionSound(), а то и ещё померзостней.
При помощи синглтона это всё делается проще. Мы создадим статическую переменную instance в классе SoundManager, и при инициализации класса будем, во-первых, сохранять в неё свежесозданный экземпляр класса, а если эта переменная непуста, то выдавать ошибку, т.к. кто-то явно пытается создать второй экзепляр SoundManager.
Существует стопицоттыщмильёнов реализаций синглтона и, похоже, каждый программист считает долгом придумать свою. Я опишу пару очень простых, без изысков.
Уже неплохо. Теперь единожды где-нибудь после старта приложения создаём экземпляр этого класса при помощи New SoundManager, и обращаемся к нему из любого места так: SoundManager.instance.playExplosionSound();
А что будет если попробовать сделать ещё раз New SoundManager? Получим сразу же эррор: «Class is singleton!».
Но есть и недостаток: во-первых, ничто не помешает нам сделать SoundManager.instance = null (мало ли что вздумается вашей тёмной злой стороне), и, во-вторых, класс надо не забыть инициализировать перед первым его использованием. На такой случай можно использовать другую реализацию синглтона:
Теперь ничего нигде не нужно инициализировать и создавать, знай используй себе SoundManager.instance где угодно в коде.
Однако должен отметить, что маститые специалисты не рекомендуют злоупотреблять синглтонами, т.к. это создает неявные зависимости, которые сложно сразу выявить, просто пробежавшись по списку переменных, и (что уже не столь важно для наших масштабов кода) усложняет по словам Википедии модульное тестирование.
Спасибо за внимание!

Согласно одному из определений, паттерн проектирования — это пример наиболее удачного проектного решения в области объектно-ориентированного программирования, описание взаимодействия объектов и классов для решения стандартной задачи проектирования. Другими словами — это наиболее удачный подход к построению структуры классов и взаимосвязи между ними для решения некой стандартной задачи в проектировании программы.
Синглтон («Singleton» — одиночка) используется в случае, если на все приложение необходим только один экземпляр определенного класса, доступ к которому можно будет иметь из любого места.
Предположим, что мы имеем некий класс SoundManager, который отвечает за воспроизведение всех звуков в игре. Скажем, есть у него публичный метод playExplosionSound(). До того, как мне стало известно о синглтоне, для того, чтобы вызвать этот метод из какого-нибудь глубоко упрятанного класса, приходилось делать что-то вроде myParent.myParent.soundManager.playExplosionSound(), а то и ещё померзостней.
При помощи синглтона это всё делается проще. Мы создадим статическую переменную instance в классе SoundManager, и при инициализации класса будем, во-первых, сохранять в неё свежесозданный экземпляр класса, а если эта переменная непуста, то выдавать ошибку, т.к. кто-то явно пытается создать второй экзепляр SoundManager.
Существует стопицоттыщмильёнов реализаций синглтона и, похоже, каждый программист считает долгом придумать свою. Я опишу пару очень простых, без изысков.
package
{
public class SoundManager
{
public static var instance: SoundManager;
public function SoundManager()
{
if (instance) throw new Error ("Class is singleton!")
else instance = this;
}
public function playExplosionSound():void
{
trace("Ба-бах!");
}
}
}
Уже неплохо. Теперь единожды где-нибудь после старта приложения создаём экземпляр этого класса при помощи New SoundManager, и обращаемся к нему из любого места так: SoundManager.instance.playExplosionSound();
А что будет если попробовать сделать ещё раз New SoundManager? Получим сразу же эррор: «Class is singleton!».
Но есть и недостаток: во-первых, ничто не помешает нам сделать SoundManager.instance = null (мало ли что вздумается вашей тёмной злой стороне), и, во-вторых, класс надо не забыть инициализировать перед первым его использованием. На такой случай можно использовать другую реализацию синглтона:
package
{
public class SoundManager
{
private static var _instance: SoundManager;
public function SoundManager()
{
if (_instance) throw new Error ("Class is singleton!");
}
public function playExplosionSound():void
{
trace("Ну бабах же!");
}
static public function get instance():SoundManager
{
if (!_instance) _instance = new SoundManager();
return _instance;
}
}
}
Теперь ничего нигде не нужно инициализировать и создавать, знай используй себе SoundManager.instance где угодно в коде.
Однако должен отметить, что маститые специалисты не рекомендуют злоупотреблять синглтонами, т.к. это создает неявные зависимости, которые сложно сразу выявить, просто пробежавшись по списку переменных, и (что уже не столь важно для наших масштабов кода) усложняет по словам Википедии модульное тестирование.
Спасибо за внимание!
- +9
- SeeD
Комментарии (100)
[code]
SoundManager.PlayTestSound();
[/code]
например?
Допустим для звукоменеджера — это вряд ли, но могут быть объекты которые сегодня синглетон, а завтра уже нет. У меня так было, когда я сделал игру синглетоном, а потом захотел иметь две игры одновременно.
Но писать SoundManager.instance.tralala долго и нудно. И не решает проблему, если все же захочется иметь два саундменеджера. Лучше всего, чтобы в классе использующем SoundManager, была переменная soundManager, к которой и обращаться в коде.
А чтобы не геморроится с зависимостями и кто кого когда создает, желательно освоить концепции лежащие в основе RobotLegs например. Но я пока на практике это все не применял :-) Зато в теории очень красиво и просто.
Сам класс статическим быть не может, статической может быть только метод или свойство в нём. А статический метод, в свою очередь, не может иметь доступа к нестатическим свойствам класса, которые создаются только после создания экземпляра класса.
Т.е. такое, например, не пройдёт
Вопрос же был повесить слушатель, а это уже отмазки пошли )
Не опишешь чуть подробнее в контексте игр?
И да, абстракцию — фтопку. У меня только утилиты и базовые классы кочуют из приложения в приложение. А так чтобы куски кода — не припомню. Да и смысла в этом нет, потому что при соблюдении установленных правил новые окна игры, гуй и прочие элементы создаются с нуля как по маслу.
1. маленьких хороших примеров MVC не бывает
2. пример любого MVC вызовет массу новых вопросов, обсуждений и холиваров, типа «кто заведует логикой: модель или контрололер?!» или «если контроллер такой тонкий, зачем он вообще нужен» и далее в таком духе
Если есть желание окунуться в эту тему, рекомендую топик на Flasher.ru, который предоставляет читателю много страниц вопросов, споров, ответов, советов, практических примеров и прочих в конечном счете познавательных и полезных суждений. Именно после этой темы я начал активно использовать MVC в качестве основы моих приложений.
P.S.
И я походу еще кромешный нуб в этом плане. Просто нравится. Просто использую. Просто рекомендую :)
Во-первых, упоминание о синглтоне всегда порождает эпик срач.
Во-вторых, его уже где только не описывали все кому не лень.
P.S.
Картинка доставляет ;D
Можно ещё просто сделать глобальную (ох, сейчас полетят камни) переменную soundManager.as:
или
Я вижу плохое в скрытых зависимостях. Так бы взял класс да перенёс бы целиком в другой проект — а тут вдруг бац! и выясняется, что он завязан каким-то странным образом с ещё другими классами, которые хз как связаны, в свою очередь, между собой.
Где то в коде моего класса:
Значит, чтобы избежать лишних связей, нужно иметь ссылку на саунд менеждер внутри класса.
И передавать ее, например, через конструктор.
Таким образом наш класс будет связан только с интерфейсом ISoundManager.
Это «противоположность» синглтону, или есть еще способы?
Статика очень вредна при росте проекта, при рефакторинге чаще всего от нее отказываешься или создаешь еще большие уровни абстракции типа StaticManager, StaticManagerImpl, StaticManagerImpl2 и т.д.
А если человек наносит себе увечья отверткой или молотком, то это проблемы его, а не инструмента.
Впрочем, да, им нужно уметь пользоваться. Хотя бы для того, чтобы знать, что пользоваться им не стоит.
Часто человек может даже не зная шаблонов их использовать, если он дозрел и сам понял.
А вот обратная ситуация, когда начитавшись шаблонов их вставляют везде для того, чтобы быть крутым («Я использую шаблоны») — это зло.
Тут два варианта. Ну, самых очевидных. Возможно, их больше. Первый — это антипаттерн God Object (а потому нужно не лепить сюда синглетоны, а подумать над архитектурой приложения). Второй — на самом деле можно этот объект передать в методы просто дополнительным параметром (как в случае, с саунд менеджером).
Хорошая, кстати, книжка.
А я использую синглтон и считаю его вполне удобным.
Есть, допустим, у тебя игра. В ней стопятьсот классов, всякие там методы в них есть, местами используется этот ваш синглетон для сайнд менеджера. И всё здорово, вроде бы:
До тех пор, пока не приходит тимлид и не объявляет о том, что в проекте сильно много багов, а потому решили в срочном порядке понафигачить кучу юнит тестов на все случаи жизни. И тебе, как ярому адепту синглетонов, нужно сделать так, чтобы в сборке для тестирования во всех этих стапятистах классах был не реальный инстанс саунд менеджера, а его фейк, который бы звуки не играл, но логгировал вызовы метода play.
Твои действия?
Многие здесь работают в коллективе программистов?
А вдруг завтра надо будет переписать на другом языке все?
Нужно решать проблемы по мере поступления, а не пытаться предусмотреть все.
как правило реального коддинга в приложении лишь 30%, остальные 70% — проектирование.
если вы изначально спроектировали приложение так, что чтоб его оттестировать приходится страдать вот такой канителью, как вы описали, то смысл такого проектирования?
Страдать канителью приходится при плохом проектировании, да. Синглетон — признак плохого проектирования, да. Если бодяжить в код синглетоны, то потом сложно будет его тестировать, да.
Тебе нужно загрузить конфиг.хмл и получать к нему доступ в любое время из любой точки программы, цапая оттуда нужные тебе данные. Как поступим?
и в каждый класс передаем ссылки на хмл?
хороший подход.
не пойму в чем преимущество такого подхода.
Ты меня не убедил, что синглтоны зло.
Viva la Flash
мы про синглтон и говорим, что там внутри статик переменная.
Вы серьезно?
Где здесь нужно инициализировать этот класс отдельно если при первом к нему обращении он создает свой экземпляр?
Ты же отвечаешь на этот пост и приводишь в пример синглетон, мол, как его инициализировать отдельно. Да никак. Это ж
бубль гумсинглетон.И еще на заметку просто вызвать конструктор, т.е. создать его instance способом
у вас не получится так как в него передается вспомогательный класс видимый только из самого класса Resource.
А ты тут с синглетоном…
Т.е. всякие есть, но нормальных все-таки больше.
Вот тут с пятого абзаца habrahabr.ru/post/102620/
Вот тут Именно та диаграмма про два часа blogerator.ru/page/skolko-v-den-zanimaetsja-kodirovaniem-zapadnyj-programmist. Конечный пруф лень искать да и не могу пока
— На сколько читал, вся Cтатика инициализируется при запуске программы,
отнимая ресурсы и занимая оперативную память, даже если вы еще продолжительное время не будите ею пользоваться.Синглтон же занимает ресурсы компьютера, именно в момент первого обращения к нему.
— Синглтон хорош, если требуется четкий вызов последовательности функций (напр. для инициализации). Т.е. в конструктор Синглтона можно вставить инициализирующий код. В случае статики (если статик переменные данного класса используются в его же функциях), если пользователь использует рабочую функцию, при этом не вызвав заранее функцию инициализации (конструктора тут нет), будет ошибка (или же заранее надо прописать вывод ошибки, требующий сначала вызвать функцию инициализации).
Приводится таблица отличий статики от Синглтона.
Минусы:
Статика и Синглтон увеличивает зависимость между классами,
что чрезвычайно плохо при отладке программы, многократности использования кода и адаптации классов под новые условия.
Статику и Синглтон можно заменить другими паттернами проектирования.
К примеру, если требуется «сундук с данными» (обычно это статик переменные: номер уровня, очки и пр.), которые используют много различных классов в программе/игре, то можно применить паттерн Наблюдатель (Observer), для централизации, своевременной целостности и общедоступности данных.
А вообще, чтобы максимально снизить зависимость между классами, получать удовольствие от архитектуры, которая всегда готова к новым изменениям, то надо минимизировать в программе синглтоны, статику, наследование (в зависимости от ситуации) и в противовес применять композицию и паттерны проектирования снижающие зависимости (Стратегия, Наблюдатель, Адаптер, Состояние, Декоратор, Компоновщик и пр., а также связку некоторых из них — модель MVC).
Один из самых мной любимых паттернов, это паттерн Стратегия, часто приходилось использовать, он отлично демонстрирует мощь ООП и композиции.
К слову, паттерны проектирования очень помогают мне.
Таская код движка от игры к игре, новая адаптация и его расширение
— занимают минимум времени и доставляют большое эстетическое удовольствие,
т.к уже работаешь не рамках кучи мало связанного кода,
а в рамках системы с правилами и гибкой адаптацией.
Первое отличие — вроде бы и отличие, но на практике, сколько я не наблюдал синглетонов, все они начинают юзаться если не с самого начала работы программы, то почти с самого. Да и не видел я такого, чтобы вот этот сэкономленный промежуток от старта программы до инициализации синглетона как-то уж лечил.
Второе отличие — некорректно, ибо существуют статические конструкторы. Не знаю, правда, как в этом во флеше. Если же их в языке нет, можно писать в каждом методе что-то типа
В данном случае, можно выдать сообщение об ошибке «Предварительно вызовите ф-ю Init», но Синглтон решает эту задачу лучше.