Одиночка (шаблон проектирования)
Одиночка | |
---|---|
Singleton | |
Тип | порождающий |
Плюсы | организует API; неявно загружает нужные модули в нужном порядке; оставляет место для второго похожего объекта |
Минусы | усложняет тестирование, многопоточность и отслеживание задержек; одиночки не должны неявно зависеть друг от друга |
Описан в Design Patterns | Да |
Одиночка (англ. Singleton) — порождающий шаблон проектирования, гарантирующий, что в приложении будет единственный экземпляр некоторого класса, и предоставляющий глобальную точку доступа к этому экземпляру.
Цель
[править | править код]У класса есть только один экземпляр, и он предоставляет к нему глобальную точку доступа. При попытке создания данного объекта он создаётся только в том случае, если ещё не существует, в противном случае возвращается ссылка на уже существующий экземпляр и нового выделения памяти не происходит. Существенно то, что можно пользоваться именно экземпляром класса, так как при этом во многих случаях становится доступной более широкая функциональность. Например, к описанным компонентам класса можно обращаться через интерфейс, если такая возможность поддерживается языком.
Глобальный «одинокий» объект — именно объект (log().put("Test");
), а не набор процедур, не привязанных ни к какому объекту (logPut("Test");
) — бывает нужен, если:
- Используется существующая объектно-ориентированная библиотека и ей нужен объект, унаследованный от определённого класса/интерфейса.
- Есть шансы, что один объект когда-нибудь превратится в несколько[1].
- Дополнительные факторы, исполнимые в обеих концепциях, но хорошо сочетающиеся с методикой ООП:
- Интерфейс объекта (например, игрового мира) слишком сложен, и объект/префикс
log
служит для организации API. - В зависимости от каких-нибудь условий и настроек, создаётся один из нескольких объектов. Например, в зависимости от того, ведётся лог или нет, создаётся настоящий объект, пишущий в файл, или «заглушка», ничего не делающая.
- Создание объекта занимает время, и для красоты объект можно создавать, когда на экране уже что-то видно.
- Интерфейс объекта (например, игрового мира) слишком сложен, и объект/префикс
Такие объекты можно создавать и при инициализации программы. Это может приводить к следующим трудностям:
- Если объект нужен уже при инициализации, он может быть затребован раньше, чем будет создан.
- Бывает, что объект нужен не всегда. В таком случае его создание можно пропустить. Особенно это важно, если одиночек (например, диалоговых окон) много — тогда пользователь быстро получит интерфейс, а окна будут создаваться по одному, не мешая работе пользователя.
Одиночка может принадлежать и не глобальному пространству имён, а какому-то объекту — например, главной форме Qt.
Плюсы
[править | править код]- Наведение порядка в глобальном пространстве имён.
- Ускорение начального запуска программы, если есть множество одиночек, которые не нужны для запуска. Особенно удачно выходит, если создание всех «одиночек» даёт ощутимую задержку, а создание каждого отдельного — практически незаметно.
- Ускоряется и неуправляемая инициализация программы (до тела): будут запущены только те модули, которые действительно там нужны. А когда тело получит управление, можно хотя бы нарисовать заставку.
- Упрощение кода инициализации — система автоматически неявно отделит нужные компоненты от ненужных и проведёт топологическую сортировку.
- Одиночку можно в дальнейшем превратить в шаблон-стратегию или несколько таких объектов[1].
- Пример шаблона-стратегии: запись журнала действий в файл или в никуда.
- Пример нескольких объектов: размножив классы
Player
иRenderer
, можно сделать игру вдвоём на одной машине.
Минусы
[править | править код]- Усложняется контроль за межпоточными гонками и задержками.
- Многопоточного «одиночку» сложно писать «из головы»[1][2]: доступ к давно построенному одиночке в идеале не должен открывать мьютекс. Лучше проверенные решения. Как пример см. преамбулу к статье Модель памяти Java.
- Конфликт двух потоков за недостроенного одиночку приведёт к задержке.
- Если объект создаётся долго, задержка может мешать пользователю или ещё как-то нарушать реальное время. В таком случае его создание лучше перенести в старт программы.
- Если программа стартует долго, сложнее становится сделать строку прогресса.
- Сам факт, что две далёкие друг от друга функции неявно полагаются на одного одиночку, может оказаться плохой архитектурой[2].
- Требуются особые функции для модульного тестирования, чтобы физически изолировать тесты[3] — например, сделать менеджер одиночек не единственным[3]. Впрочем, одиночками часто являются модули общения с аппаратурой, объектами ОС[1], программным окружением и пользователем, которые модульному тестированию поддаются плохо.
- Требуется особая тактика тестирования готовой программы, ведь пропадает даже понятие «простейшая запускаемость» — запускаемость зависит от конфигурации.
- Маленький объект без данных — чистый шаблон-стратегию или null object — обычно держат в сегменте данных, а не в динамической памяти, и превращают в одиночку в особых случаях.
- Сами по себе одиночки никак не заведуют порядком выгрузки. Возможна даже ситуация «сборщик мусора уничтожил одиночку, клиент создал нового»[1]. Некоторые языки (Паскаль) гарантируют порядок уничтожения модулей, а если нет — можно его обеспечить сторонними средствами, или при выходе явно прекратить всю подозрительную деятельность.
- Компоненты не должны иметь неявных связей между собой, иначе небольшое изменение — в программном коде, файле настроек, сценарии пользования — может спутать порядок и вызвать трудноуловимую ошибку. Пример: одиночка А использует COM, но полагается на
CoInitialize
, вызванный одиночкой Б, и без него работать не может. Решение: сделать одиночку CoInit, который явно используется и А, и Б.- Одиночки требуют особого внимания, если один из компонентов заведомо ненадёжен и для адекватной работы требует особых условий («разглючек»): библиотека, которая иногда портит память; сеть, которая разрывает соединение, если слишком долго ждать; типографский движок, способный загрузить шрифты в одном порядке и не способный наоборот… Тогда, в зависимости от порядка инициализации, компонент может сработать адекватно или нет.
Применение
[править | править код]- должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам;
- единственный экземпляр должен расширяться путём порождения подклассов, и клиентам нужно иметь возможность работать с расширенным экземпляром без модификации своего кода.
Возможные замены
[править | править код]- Просто создать объект при запуске[2]. Если объект гарантированно нужен всегда, можно и не делать его одиночкой.
- Внедрение зависимости — создать объект, а потом как-то передать его всем, кто им пользуется[2][4].
Примеры использования
[править | править код]- Ведение отладочного файла для приложения.
- В любом приложении для iOS существует класс AppDelegate, реагирующий на системные события.
Примеры реализации
[править | править код]Java 1.6
[править | править код]public class Singleton {
private static Singleton instance;
private Singleton () {};
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Java
[править | править код]Этот вариант блокирует метод getInstance() только на момент создания экземпляра. Дальнейшие обращения к методу не будут его блокировать за счет двойной проверки. Volatile не позволяет конкурентным потокам видеть промежуточное значение instance, что позволяет избежать ошибок при возвращении результата.
public class Singleton {
private volatile Singleton instance;
private Singleton() {}
public Singleton getInstance() {
if (instance == null) {
synchronized (this) {
if (instance == null) {
return new Singleton();
}
}
}
return instance;
}
}
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
// В этом блоке возможна обработка исключений
}
private Singleton () {}
public static Singleton getInstance() {
return instance;
}
}
Java 1.5
[править | править код]public class Singleton {
private Singleton() {}
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
public enum SingletonEnum {
INSTANCE;
public void someMethod() {
***
}
public void anotherMethod() {
***
}
}
Python
[править | править код]Из PEP 0318 Архивная копия от 3 июня 2020 на Wayback Machine:
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
Из PEP 0318 Архивная копия от 3 июня 2020 на Wayback Machine[нет в источнике]:
class MetaSingleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class MyClass(metaclass=MetaSingleton):
...
C++
[править | править код]Ниже приведена одна из возможных реализаций паттерна Одиночка на C++ (известная как синглтон Майерса), где одиночка представляет собой статический локальный объект. Важным моментом является то, что конструктор класса объявлен как private
, что позволяет предотвратить создание экземпляров класса за пределами его реализации. Помимо этого, закрытыми также объявлены конструктор копирования и оператор присваивания. Последние следует объявлять, но не определять, так как это позволяет в случае их случайного вызова из кода получить легко обнаруживаемую ошибку компоновки. Отметим также, что приведенный пример не является потокобезопасным в C++03, для работы с классом из нескольких потоков нужно защитить переменную theSingleInstance
от одновременного доступа, например, с помощью мьютекса или критической секции. Впрочем, в C++11 синглтон Майерса является потокобезопасным и без всяких блокировок.
class OnlyOne
{
public:
static OnlyOne& Instance()
{
static OnlyOne theSingleInstance;
return theSingleInstance;
}
private:
OnlyOne(){}
OnlyOne(const OnlyOne& root) = delete;
OnlyOne& operator=(const OnlyOne&) = delete;
};
Ещё один пример реализации одиночки на C++ с возможностью наследования для создания интерфейса, каркасом которого послужит, собственно, одиночка. Временем «жизни» единственного объекта удобно управлять, используя механизм подсчета ссылок.
class Singleton
{
protected:
static Singleton* _self;
Singleton() {}
virtual ~Singleton() {}
public:
static Singleton* Instance()
{
if(!_self)
{
_self = new Singleton();
}
return _self;
}
static bool DeleteInstance()
{
if(_self)
{
delete _self;
_self = 0;
return true;
}
return false;
}
};
Singleton* Singleton ::_self = 0;
C#
[править | править код]Максимально простой способ реализации потокобезопасного и ленивого синглтона, требующего, однако, .NET версии 4 и более.
public sealed class Singleton
{
private static readonly Lazy<Singleton> instanceHolder =
new Lazy<Singleton>(() => new Singleton());
private Singleton()
{
...
}
public static Singleton Instance
{
get { return instanceHolder.Value; }
}
}
Для отложенной инициализации Singleton'а в C# рекомендуется использовать конструкторы типов (статический конструктор). CLR автоматически вызывает конструктор типа при первом обращении к типу, при этом обеспечивая безопасность в отношении синхронизации потоков. Конструктор типа автоматически генерируется компилятором и в нем происходит инициализация всех полей типа (статических полей). Явно задавать конструктор типа не следует, так как в этом случае он будет вызываться непосредственно перед обращением к типу и JIT-компилятор не сможет применить оптимизацию (например, если первое обращение к Singleton'у происходит в цикле).
/// generic Singleton<T> (потокобезопасный с использованием generic-класса и с отложенной инициализацией)
/// <typeparam name="T">Singleton class</typeparam>
public class Singleton<T> where T : class
{
/// Защищённый конструктор необходим для того, чтобы предотвратить создание экземпляра класса Singleton.
/// Он будет вызван из закрытого конструктора наследственного класса.
protected Singleton() { }
/// Фабрика используется для отложенной инициализации экземпляра класса
private sealed class SingletonCreator<S> where S : class
{
//Используется Reflection для создания экземпляра класса без публичного конструктора
private static readonly S instance = (S) typeof(S).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new Type[0],
new ParameterModifier[0]).Invoke(null);
public static S CreatorInstance
{
get { return instance; }
}
}
public static T Instance
{
get { return SingletonCreator<T>.CreatorInstance; }
}
}
/// Использование Singleton
public class TestClass : Singleton<TestClass>
{
/// Вызовет защищённый конструктор класса Singleton
private TestClass() { }
public string TestProc()
{
return "Hello World";
}
}
Также можно использовать стандартный вариант потокобезопасной реализации Singleton с отложенной инициализацией:
public class Singleton
{
/// защищённый конструктор нужен, чтобы предотвратить создание экземпляра класса Singleton
protected Singleton() { }
private sealed class SingletonCreator
{
private static readonly Singleton instance = new Singleton();
public static Singleton Instance { get { return instance; } }
}
public static Singleton Instance
{
get { return SingletonCreator.Instance; }
}
}
Если нет необходимости в каких-либо публичных статических методах или свойствах (кроме свойства Instance), то можно использовать упрощенный вариант:
public class Singleton
{
private static readonly Singleton instance = new Singleton();
public static Singleton Instance
{
get { return instance; }
}
/// защищённый конструктор нужен, чтобы предотвратить создание экземпляра класса Singleton
protected Singleton() { }
}
Пример с ленивой инициализацией
namespace Singleton{
public class Singleton
{
private static Singleton instance;
public static Singleton Instance
{
get
{
instance ??= new Singleton();
return instance;
}
}
protected Singleton() { }
}
}
PHP 4
[править | править код]<?php
class Singleton {
function Singleton( $directCall = true ) {
if ( $directCall ) {
trigger_error("Нельзя использовать конструктор для создания класса Singleton.
Используйте статический метод getInstance()",E_USER_ERROR);
}
//TODO: Добавьте основной код конструктора здесь
}
function &getInstance() {
static $instance;
if ( !is_object( $instance ) ) {
$class = __CLASS__;
$instance = new $class( false );
}
return $instance;
}
}
//usage
$test = &Singleton::getInstance();
?>
PHP 5
[править | править код]<?php
class Singleton {
private static $instance; // экземпляр объекта
private function __construct(){ /* ... @return Singleton */ } // Защищаем от создания через new Singleton
private function __clone() { /* ... @return Singleton */ } // Защищаем от создания через клонирование
private function __wakeup() { /* ... @return Singleton */ } // Защищаем от создания через unserialize
public static function getInstance() { // Возвращает единственный экземпляр класса. @return Singleton
if ( empty(self::$instance) ) {
self::$instance = new self();
}
return self::$instance;
}
public function doAction() { }
}
/*
Применение
*/
Singleton::getInstance()->doAction(); //
?>
PHP 5.4
[править | править код]<?php
trait Singleton {
private static $instance = null;
private function __construct() { /* ... @return Singleton */ } // Защищаем от создания через new Singleton
private function __clone() { /* ... @return Singleton */ } // Защищаем от создания через клонирование
private function __wakeup() { /* ... @return Singleton */ } // Защищаем от создания через unserialize
public static function getInstance() {
return
self::$instance===null
? self::$instance = new static() // Если $instance равен 'null', то создаем объект new self()
: self::$instance; // Иначе возвращаем существующий объект
}
}
/**
* Class Foo
* @method static Foo getInstance()
*/
class Foo {
use Singleton;
private $bar = 0;
public function incBar() {
$this->bar++;
}
public function getBar() {
return $this->bar;
}
}
/*
Применение
*/
$foo = Foo::getInstance();
$foo->incBar();
var_dump($foo->getBar());
$foo = Foo::getInstance();
$foo->incBar();
var_dump($foo->getBar());
?>
Delphi
[править | править код]Для Delphi 2005 и выше подходит следующий пример (не потоко-безопасный):
type
TSingleton = class
strict private
class var
Instance: TSingleton;
public
class function NewInstance: TObject; override;
end;
class function TSingleton.NewInstance: TObject;
begin
if not Assigned(Instance) then
Instance := TSingleton(inherited NewInstance);
Result := Instance;
end;
Для более ранних версий следует переместить код класса в отдельный модуль, а объявление Instance
заменить объявлением глобальной переменной в его секции implementation
(до Delphi 7 включительно секции class var
и strict private
отсутствовали).
Dart
[править | править код]На основе фабричного конструктора из документации Dart
class Singleton {
static final Singleton _singleton = Singleton._internal();
factory Singleton() {
return _singleton;
}
Singleton._internal();
}
Io
[править | править код]Singleton := Object clone
Singleton clone := Singleton
Ruby
[править | править код]class Singleton
def self.new
@instance ||= super
end
end
В стандартную библиотеку (Ruby 1.8 и выше) входит модуль Singleton, что позволяет создавать синглтоны ещё проще:
require 'singleton'
class Foo
include Singleton
end
a = Foo.instance # Foo.new недоступен, для получения ссылки на (единственный)
# экземпляр класса Foo следует использовать метод Foo#instance
Common Lisp
[править | править код](defclass singleton-class () ;;метакласс, реализующий механизм синглтона
((instance :initform nil)))
(defmethod validate-superclass ((class singleton-class) (superclass standard-class))
t) ;;Разрешаем наследование классов-синглтонов от обычных классов
(defmethod validate-superclass ((class singleton-class) (superclass singleton-class))
t) ;;Разрешаем наследование классов-синглтонов от других классов-синглтонов
(defmethod validate-superclass ((class standard-class) (superclass singleton-class))
nil) ;;Запрещаем наследование обычных классов от синглтонов
(defmethod make-instance ((class singleton-class) &key)
(with-slots (instance) class
(or instance (setf instance (call-next-method)))))
(defclass my-singleton-class ()
()
(:metaclass singleton-class))
VB.NET
[править | править код]Module Program
Sub Main()
Dim T1 As Singleton = Singleton.getInstance
T1.Value = 1000
Dim T2 As Singleton = Singleton.getInstance
Console.WriteLine(T2.Value)
Console.Read()
End Sub
End Module
Public Class Singleton
Public Value As Integer
'Не разрешаем конструктор
Protected Sub New()
End Sub
Private NotInheritable Class SingletonCreator
Private Shared ReadOnly m_instance As New Singleton()
Public Shared ReadOnly Property Instance() As Singleton
Get
Return m_instance
End Get
End Property
End Class
Public Shared ReadOnly Property getInstance() As Singleton
Get
Return SingletonCreator.Instance
End Get
End Property
End Class
Perl
[править | править код]use v5.10;
use strict;
package Singleton;
sub new {
# Объявление статической переменной $instance
# и возврат её как результат выполнения метода new
state $instance = bless {};
}
package main;
my $a = Singleton->new;
my $b = Singleton->new;
say "$a $b"; # Ссылки $a и $b указывают на один объект
#!/usr/bin/perl -w
use feature "say";
use strict;
use warnings;
package Singleton {
my $instance; # экземпляр класса (статическое поле)
# -- ** конструктор ** --
sub new {
my $class = shift;
unless($instance) { # проверяем нет ли уже созданного экземпляра класса
$instance = { # если нет, создаем новый и записываем в него имя того, с кем надо поздороваться
name => shift,
};
bless $instance, $class;
}
return $instance; # возвращаем единственный и неповторимый экземпляр нашего класса
}
# -- ** приветствие ** --
sub hello {
my($self) = (shift);
say "Hello, $self->{name}"; # давайте поприветствуем хозяина этого объекта
}
}
my $a = Singleton->new('Alex'); # создаем экземпляр класса с именем Alex
my $b = Singleton->new('Barney'); # ... а теперь пытаемся создать ещё один экземпляр для Barney
$a->hello(); # Hello, Alex # да, здравствуй, Алекс
$b->hello(); # Hello, Alex # ой, Барни, извини, какое недоразумение...
ActionScript 3
[править | править код]Вариант с приватным классом:
package
{
public class Singleton
{
private static var _instance:Singleton;
public function Singleton(privateClass:PrivateClass)
{
}
public static function getInstance():Singleton
{
if (!_instance)
_instance = new Singleton(new PrivateClass());
return _instance;
}
}
}
// Из-за того, что класс объявлен в том же файле за пределами
// пакета, использовать его сможет только класс Singleton.
class PrivateClass
{
public function PrivateClass()
{
}
}
Вариант с вызовом исключения:
package
{
public class Singleton
{
public static const instance:Singleton = new Singleton();
public function Singleton()
{
// Boolean(Singleton) равно false, в случае если экземпляр класса
// будет создан до выполнения статического конструктора
if (Singleton)
throw new Error("Class is singleton.");
}
}
}
Вариант с переменной доступа:
package {
public class MySingleton {
private static var _instance:MySingleton;
//Переменная доступа
private static var _isConstructing:Boolean;
public function MySingleton() {
if (!_isConstructing) throw new Error("Singleton, use MySingleton.instance");
}
public static function get instance():MySingleton {
if (_instance == null) {
_isConstructing = true;
_instance = new MySingleton();
_isConstructing = false;
}
return _instance;
}
}
}
Преимущества варианта с приватным классом:
- При попытке использовать конструктор напрямую, ошибка будет выявлена компилятором сразу же. // Не является преимуществом только этого метода
- Создание объекта происходит по запросу.
Недостаток варианта с приватным классом:
- Можно подменить приватный класс своим собственным с таким же названием.
Преимущества варианта с использованием исключения:
- Меньше кода.
CoffeeScript
[править | править код]Классический подход (Coffeescript ≠ 1.5)
class Singleton
instance = undefined
constructor : ->
if instance?
return instance
else instance = @
# Код конструктора
console.assert( new Singleton is new Singleton );
Подход, основанный на возможности доступа к функции из её тела (Coffeescript ≠ 1.5)
class Singleton
init = -> # конструктор как приватный метод класса
# Код конструктора
# ...
# Заменяем конструктор, сохраняя this (@)
init = => @
return @
# Реальный конструктор. Служит для вызова init
# return использовать обязательно, иначе вернёт this (@)
constructor : -> return init.apply(@, arguments)
console.assert( new Singleton is new Singleton )
constructor : -> Singleton = => @
Однако, если использовать пространства имён, то возможен такой вариант:
ns = {}
class ns.Singleton
constructor : ->
# Код конструктора
ns.Singleton = => @
console.assert( new ns.Singleton is new ns.Singleton )
JavaScript
[править | править код]Метод, основанный на сокрытии переменных с помощью замыканий. В качестве бонуса - возможность объявлять приватные методы и свойства, которые будут доступны и конструктору и методам "класса".
const Singleton = (function() {
let instance;
// Приватные методы и свойства
// Конструктор
function Singleton() {
if (instance) return instance;
instance = this;
}
// Публичные методы
Singleton.prototype.test = function() {};
return Singleton;
})();
console.log(new Singleton() === new Singleton());
Без использования сокрытия переменных есть простое решение, основанное на том, что функция Singleton является объектом. Минусом является возможность изменения свойства instance вне класса:
function Singleton() {
const instance = Singleton.instance;
if (instance) return instance;
Singleton.instance = this;
}
Singleton.prototype.test = function() {};
console.log(new Singleton() === new Singleton());
Наиболее короткий вариант.
const Singleton = new (function() {
const instance = this;
return function() { return instance; };
})();
console.log(new Singleton() === new Singleton());
С использованием статических приватных полей JS-класса:
class Singleton{
static #onlyInstance = null;
constructor(){
if(!Singleton.#onlyInstance){
Singleton.#onlyInstance = this;
} else {
return Singleton.#onlyInstance;
}
}
}
console.log(new Singleton() === new Singleton());
Objective-C
[править | править код]Singleton.h
@interface Singleton : NSObject {
}
+ (Singleton *)sharedInstance;
@end
Singleton.m
@implementation Singleton
static Singleton *_sharedInstance = nil;
+ (Singleton *)sharedInstance {
@synchronized(self) {
if (!_sharedInstance) {
_sharedInstance = [[Singleton alloc] init];
}
}
return _sharedInstance;
}
@end
Или (только для OS X 10.6+, iOS 4.0+):
@implementation Singleton
+ (Singleton *)sharedInstance {
static dispatch_once_t pred;
static Singleton *sharedInstance = nil;
dispatch_once(&pred, ^{ sharedInstance = [[self alloc] init]; });
return sharedInstance;
}
@end
Swift
[править | править код]class Singleton {
static let shared = Singleton()
private init() { }
}
Scala, Kotlin
[править | править код]object Singleton {} // ключевое слово "object" создает класс, который реализует паттерн "одиночка" по умолчанию
См. также
[править | править код]Литература
[править | править код]- Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = 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.
Ссылки
[править | править код]- Паттерн Singleton (Одиночка) Архивная копия от 17 февраля 2006 на Wayback Machine — пример использования шаблона (C++).
- Одиночка Архивная копия от 4 ноября 2005 на Wayback Machine — простое описание с примером применения.
- Реализация Singleton на Java — описание классической реализации и многопоточные модификации.
- [1] Архивировано 1 марта 2012 года. — The «Double-Checked Locking is Broken» Declaration in java
- Реализация синглтонов на Perl Архивная копия от 7 октября 2010 на Wayback Machine — пример для Perl.
- Singleton Considered Stupid Архивная копия от 17 декабря 2009 на Wayback Machine — критика паттерна Singleton
- Мультисинглтон Архивная копия от 17 февраля 2011 на Wayback Machine — фабрика синглтонов.
- Паттерн Singleton (Одиночка) Архивная копия от 2 февраля 2012 на Wayback Machine — три варианта реализации на C++.
- Классы-одиночки (недоступная ссылка) — реализация для Delphi 6/7.
- Singleton Java, Python, PHP
Примечания
[править | править код]- ↑ 1 2 3 4 5 Источник . Дата обращения: 21 ноября 2023. Архивировано 21 ноября 2023 года.
- ↑ 1 2 3 4 Drawbacks of the Singleton Design Pattern | Baeldung
- ↑ 1 2 How to write unit tests for a singleton class | Technical Feeder . Дата обращения: 21 ноября 2023. Архивировано 21 ноября 2023 года.
- ↑ https://medium.com/@shashidj206/the-singleton-design-pattern-best-practices-and-pitfalls-58f9d1c17ab7