Interested Article - Одиночка (шаблон проектирования)

Одиночка ( англ. Singleton ) — порождающий шаблон проектирования , гарантирующий, что в однопоточном приложении будет единственный экземпляр некоторого класса, и предоставляющий глобальную точку доступа к этому экземпляру.

Цель

У класса есть только один экземпляр, и он предоставляет к нему глобальную точку доступа. При попытке создания данного объекта он создаётся только в том случае, если ещё не существует, в противном случае возвращается ссылка на уже существующий экземпляр и нового выделения памяти не происходит. Существенно то, что можно пользоваться именно экземпляром класса, так как при этом во многих случаях становится доступной более широкая функциональность. Например, к описанным компонентам класса можно обращаться через интерфейс , если такая возможность поддерживается языком.

Глобальный «одинокий» объект — именно объект ( log (). put ( "Test" ); ), а не набор процедур, не привязанных ни к какому объекту ( logPut ( "Test" ); ) — бывает нужен, если:

  • Используется существующая объектно-ориентированная библиотека и ей нужен объект, унаследованный от определённого класса/интерфейса.
  • Есть шансы, что один объект когда-нибудь превратится в несколько.
  • Дополнительные факторы, исполнимые в обеих концепциях, но хорошо сочетающиеся с методикой ООП:
    • Интерфейс объекта (например, игрового мира) слишком сложен, и объект/префикс log служит для организации API.
    • В зависимости от каких-нибудь условий и настроек, создаётся один из нескольких объектов . Например, в зависимости от того, ведётся лог или нет, создаётся настоящий объект, пишущий в файл, или «заглушка», ничего не делающая .
    • Создание объекта занимает время, и для красоты объект можно создавать, когда на экране уже что-то видно.

Такие объекты можно создавать и при инициализации программы. Это может приводить к следующим трудностям:

  • Если объект нужен уже при инициализации, он может быть затребован раньше, чем будет создан.
  • Бывает, что объект нужен не всегда. В таком случае его создание можно пропустить. Особенно это важно, если одиночек (например, диалоговых окон) много — тогда пользователь быстро получит интерфейс , а окна будут создаваться по одному, не мешая работе пользователя.

Одиночка может принадлежать и не глобальному пространству имён, а какому-то объекту — например, главной форме Qt .

Плюсы

  • Наведение порядка в глобальном пространстве имён.
  • Ускорение начального запуска программы, если есть множество одиночек, которые не нужны для запуска. Особенно удачно выходит, если создание всех «одиночек» даёт ощутимую задержку, а создание каждого отдельного — практически незаметно.
  • Упрощение кода инициализации — система автоматически неявно отделит нужные компоненты от ненужных и проведёт топологическую сортировку .
  • Одиночку можно в дальнейшем превратить в шаблон-стратегию или несколько таких объектов.
    • Пример шаблона-стратегии: запись журнала действий в файл или в никуда.
    • Пример нескольких объектов: размножив классы Player и Renderer , можно сделать игру вдвоём на одной машине.

Минусы

  • Усложняется контроль за межпоточными гонками и задержками.
    • Многопоточного «одиночку» сложно писать «из головы» : доступ к давно построенному одиночке в идеале не должен открывать мьютекс . Лучше проверенные решения. Как пример см. преамбулу к статье Модель памяти Java .
    • Конфликт двух потоков за недостроенного одиночку приведёт к задержке.
    • Если объект создаётся долго, задержка может мешать пользователю или нарушать реальное время . В таком случае его создание лучше перенести в старт программы.
    • Если программа стартует долго, сложнее становится сделать строку прогресса .
  • Требуются особые функции для модульного тестирования , чтобы физически изолировать тесты — например, сделать менеджер одиночек не единственным . Впрочем, одиночками часто являются модули общения с аппаратурой, объектами ОС, программным окружением и пользователем , которые модульному тестированию поддаются плохо.
  • Требуется особая тактика тестирования готовой программы, ведь пропадает даже понятие « простейшая запускаемость » — запускаемость зависит от конфигурации.
  • Маленький объект без данных — чистый шаблон-стратегию или null object — обычно держат в , а не в динамической памяти , и превращают в одиночку в особых случаях.
  • Сами по себе одиночки никак не заведуют порядком выгрузки. Возможна даже ситуация « сборщик мусора уничтожил одиночку, клиент создал нового» . Если этот порядок важен, можно перевести систему на умные указатели или при выходе явно прекратить всю подозрительную деятельность в ключевых компонентах.
  • Компоненты не должны иметь неявных связей между собой, иначе небольшое изменение — в программном коде, файле настроек, сценарии пользования — может спутать порядок и вызвать трудноуловимую ошибку. Пример: одиночка А использует COM , но полагается на CoInitialize , вызванный одиночкой Б, и без него работать не может. Решение: сделать одиночку CoInit, который явно используется и А, и Б.
    • Одиночки требуют особого внимания, если один из компонентов заведомо ненадёжен и для адекватной работы требует особых условий («разглючек»): библиотека, которая иногда портит память; сеть, которая разрывает соединение, если слишком долго ждать; типографский движок, способный загрузить шрифты в одном порядке и не способный наоборот… Тогда, в зависимости от порядка инициализации, компонент может сработать адекватно или нет.

Применение

  • должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам;
  • единственный экземпляр должен расширяться путём порождения подклассов, и клиентам нужно иметь возможность работать с расширенным экземпляром без модификации своего кода.

Примеры использования

  • Ведение отладочного файла для приложения.
  • В любом приложении для iOS существует класс AppDelegate, реагирующий на системные события.

Примеры реализации

Java 1.6

Java

Java

Java 1.5

Java 1.5

Python

Из от 3 июня 2020 на Wayback Machine :

Python

Из от 3 июня 2020 на Wayback Machine [ нет в источнике ] :

C++

Ниже приведена одна из возможных реализаций паттерна Одиночка на C++ (известная как синглтон Майерса ), где одиночка представляет собой статический локальный объект. Важным моментом является то, что конструктор класса объявлен как private , что позволяет предотвратить создание экземпляров класса за пределами его реализации. Помимо этого, закрытыми также объявлены конструктор копирования и оператор присваивания. Последние следует объявлять, но не определять, так как это позволяет в случае их случайного вызова из кода получить легко обнаруживаемую ошибку компоновки. Отметим также, что приведенный пример не является потокобезопасным в C++03, для работы с классом из нескольких потоков нужно защитить переменную theSingleInstance от одновременного доступа, например, с помощью мьютекса или критической секции . Впрочем, в C++11 синглтон Майерса является потокобезопасным и без всяких блокировок.

Ещё один пример реализации одиночки на C++ с возможностью наследования для создания интерфейса, каркасом которого послужит, собственно, одиночка. Временем «жизни» единственного объекта удобно управлять, используя механизм подсчета ссылок .

C#

PHP 4

PHP 5

PHP 5.4

Delphi

Для Delphi 2005 и выше подходит следующий пример (не потоко-безопасный):

Для более ранних версий следует переместить код класса в отдельный модуль, а объявление 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 .
  • Эрик Фримен, Элизабет Фримен. Паттерны проектирования = Head First Design Patterns. — СПб. : Питер, 2011. — 656 с. — ISBN 978-5-459-00435-9 .

Ссылки

  • от 17 февраля 2006 на Wayback Machine — пример использования шаблона (C++).
  • от 4 ноября 2005 на Wayback Machine — простое описание с примером применения.
  • — описание классической реализации и многопоточные модификации.
  • 1 марта 2012 года. — The «Double-Checked Locking is Broken» Declaration in java
  • от 7 октября 2010 на Wayback Machine — пример для Perl .
  • от 17 декабря 2009 на Wayback Machine — критика паттерна Singleton
  • от 17 февраля 2011 на Wayback Machine — фабрика синглтонов.
  • от 2 февраля 2012 на Wayback Machine — три варианта реализации на C++.
  • (недоступная ссылка) — реализация для Delphi 6/7.

Примечания

Источник —

Same as Одиночка (шаблон проектирования)