Русская Википедия:Одиночка (шаблон проектирования)
Шаблон:Другие значения Шаблон:Шаблон проектирования
Одиночка (Шаблон:Lang-en) — порождающий шаблон проектирования, гарантирующий, что в однопоточном приложении будет единственный экземпляр некоторого класса, и предоставляющий глобальную точку доступа к этому экземпляру.
Цель
У класса есть только один экземпляр, и он предоставляет к нему глобальную точку доступа. При попытке создания данного объекта он создаётся только в том случае, если ещё не существует, в противном случае возвращается ссылка на уже существующий экземпляр и нового выделения памяти не происходит. Существенно то, что можно пользоваться именно экземпляром класса, так как при этом во многих случаях становится доступной более широкая функциональность. Например, к описанным компонентам класса можно обращаться через интерфейс, если такая возможность поддерживается языком.
Глобальный «одинокий» объект — именно объект (Шаблон:Cpp), а не набор процедур, не привязанных ни к какому объекту (Шаблон:Cpp) — бывает нужен, если:
- Используется существующая объектно-ориентированная библиотека и ей нужен объект, унаследованный от определённого класса/интерфейса.
- Есть шансы, что один объект когда-нибудь превратится в несколько.
- Дополнительные факторы, исполнимые в обеих концепциях, но хорошо сочетающиеся с методикой ООП:
- Интерфейс объекта (например, игрового мира) слишком сложен, и объект/префикс Шаблон:Cpp служит для организации API.
- В зависимости от каких-нибудь условий и настроек, создаётся один из нескольких объектов. Например, в зависимости от того, ведётся лог или нет, создаётся настоящий объект, пишущий в файл, или «заглушка», ничего не делающая.
- Создание объекта занимает время, и для красоты объект можно создавать, когда на экране уже что-то видно.
Такие объекты можно создавать и при инициализации программы. Это может приводить к следующим трудностям:
- Если объект нужен уже при инициализации, он может быть затребован раньше, чем будет создан.
- Бывает, что объект нужен не всегда. В таком случае его создание можно пропустить. Особенно это важно, если одиночек (например, диалоговых окон) много — тогда пользователь быстро получит интерфейс, а окна будут создаваться по одному, не мешая работе пользователя.
Плюсы
- Наведение порядка в глобальном пространстве имён.
- Ускорение начального запуска программы, если есть множество одиночек, которые не нужны для запуска. Особенно удачно выходит, если создание всех «одиночек» даёт ощутимую задержку, а создание каждого отдельного — практически незаметно.
- Упрощение кода инициализации — система автоматически неявно отделит нужные компоненты от ненужных и проведёт топологическую сортировку.
- Одиночку можно в дальнейшем превратить в шаблон-стратегию или несколько таких объектов.
- Пример шаблона-стратегии: запись журнала действий в файл или в никуда.
- Пример нескольких объектов: размножив классы
Player
иRenderer
, можно сделать игру вдвоём на одной машине.
Минусы
- Усложняется контроль за межпоточными гонками и задержками.
- Многопоточного «одиночку» сложно писать «из головы»: доступ к давно построенному одиночке в идеале не должен открывать мьютекс. Лучше проверенные решения. Как пример см. преамбулу к статье Модель памяти Java.
- Конфликт двух потоков за недостроенного одиночку приведёт к задержке.
- Если объект создаётся долго, задержка может мешать пользователю или нарушать реальное время. В таком случае его создание лучше перенести в инициализацию программы.
- Если программа стартует долго, сложнее становится сделать строку прогресса.
- Требуются особые функции для модульного тестирования — например, чтобы перевести библиотеку в «не-одинокий» режим и полностью изолировать тесты друг от друга. Впрочем, одиночками часто являются модули общения с аппаратурой, объектами ОС, программным окружением и пользователем, которые модульному тестированию поддаются плохо.
- Требуется особая тактика тестирования готовой программы, ведь пропадает даже понятие «простейшая запускаемость» — запускаемость зависит от конфигурации.
- Маленький объект без данных — чистый шаблон-стратегию или null object — обычно держат в сегменте данных, а не в динамической памяти, и превращают в одиночку в особых случаях.
- Сами по себе одиночки никак не заведуют порядком выгрузки — если он важен, можно перевести систему на умные указатели или при выходе явно прекратить всю подозрительную деятельность в ключевых компонентах.
- Компоненты не должны иметь неявных связей между собой, иначе небольшое изменение — в программном коде, файле настроек, сценарии пользования — может спутать порядок и вызвать трудноуловимую ошибку. Пример: одиночка А использует COM, но полагается на
CoInitialize
, вызванный одиночкой Б, и без него работать не может. Решение: сделать одиночку CoInit, который явно используется и А, и Б.- Одиночки требуют особого внимания, если один из компонентов заведомо ненадёжен и для адекватной работы требует особых условий («разглючек»): библиотека, которая иногда портит память; сеть, которая разрывает соединение, если слишком долго ждать; типографский движок, способный загрузить шрифты в одном порядке и не способный наоборот… Тогда, в зависимости от порядка инициализации, компонент может сработать адекватно или нет.
Применение
- должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам;
- единственный экземпляр должен расширяться путём порождения подклассов, и клиентам нужно иметь возможность работать с расширенным экземпляром без модификации своего кода.
Примеры использования
- Ведение отладочного файла для приложения.
- В любом приложении для iOS существует класс AppDelegate, реагирующий на системные события.
Примеры реализации
Java 1.6
Java
Java
Java 1.5
Java 1.5
Python
Из PEP 0318 Шаблон:Wayback:Шаблон:Hider hiding
Python
Из PEP 0318 Шаблон:WaybackШаблон:Нет в источнике:Шаблон:Hider hiding
C++
Ниже приведена одна из возможных реализаций паттерна Одиночка на C++ (известная как синглтон Майерса), где одиночка представляет собой статический локальный объект. Важным моментом является то, что конструктор класса объявлен как private
, что позволяет предотвратить создание экземпляров класса за пределами его реализации. Помимо этого, закрытыми также объявлены конструктор копирования и оператор присваивания. Последние следует объявлять, но не определять, так как это позволяет в случае их случайного вызова из кода получить легко обнаруживаемую ошибку компоновки. Отметим также, что приведенный пример не является потокобезопасным в C++03, для работы с классом из нескольких потоков нужно защитить переменную theSingleInstance
от одновременного доступа, например, с помощью мьютекса или критической секции. Впрочем, в C++11 синглтон Майерса является потокобезопасным и без всяких блокировок.
Шаблон:Hider hiding
Ещё один пример реализации одиночки на C++ с возможностью наследования для создания интерфейса, каркасом которого послужит, собственно, одиночка. Временем «жизни» единственного объекта удобно управлять, используя механизм подсчета ссылок.
C#
PHP 4
PHP 5
PHP 5.4
Delphi
Для Delphi 2005 и выше подходит следующий пример (не потоко-безопасный):
Шаблон:Hider hiding
Для более ранних версий следует переместить код класса в отдельный модуль, а объявление Instance
заменить объявлением глобальной переменной в его секции implementation
(до Delphi 7 включительно секции class var
и strict private
отсутствовали).
Dart
Io
Ruby
Common Lisp
VB.NET
Perl
Perl
ActionScript 3
CoffeeScript
JavaScript
Objective-C
Swift
Scala, Kotlin
См. также
Литература
- Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М.: «Вильямс», 2002. — С. 288. — ISBN 0-201-71594-5.
- Шаблон:Книга
Ссылки
- Паттерн Singleton (Одиночка) Шаблон:Wayback — пример использования шаблона (C++).
- Одиночка Шаблон:Wayback — простое описание с примером применения.
- Реализация Singleton на Java — описание классической реализации и многопоточные модификации.
- [1] Шаблон:Архивировано — The «Double-Checked Locking is Broken» Declaration in java
- Реализация синглтонов на Perl Шаблон:Wayback — пример для Perl.
- Singleton Considered Stupid Шаблон:Wayback — критика паттерна Singleton
- Мультисинглтон Шаблон:Wayback — фабрика синглтонов.
- Паттерн Singleton (Одиночка) Шаблон:Wayback — три варианта реализации на C++.
- Классы-одиночкиШаблон:Недоступная ссылка — реализация для Delphi 6/7.
- Singleton Java, Python, PHP
Примечания
Шаблон:Типы шаблонов проектирования