- Одиночка (шаблон проектирования)
-
Шаблон проектирования Одиночка Singleton Тип: порождающий
Описан в Design Patterns Да
Одиночка (англ. Singleton) в программировании — порождающий шаблон проектирования.
Цель
Гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа. Существенно то, что можно пользоваться именно экземпляром класса, так как при этом во многих случаях становится доступной более широкая функциональность. Например, к описанным компонентам класса можно обращаться через интерфейс, если такая возможность поддерживается языком.
Плюсы
- контролируемый доступ к единственному экземпляру;
Минусы
- глобальные объекты могут быть вредны для объектного программирования, в некоторых случаях приводя к созданию немасштабируемого проекта.
- усложняет написание модульных тестов и следование TDD
Применение
- должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам;
- единственный экземпляр должен расширяться путем порождения подклассов, и клиентам нужно иметь возможность работать с расширенным экземпляром без модификации своего кода.
Пример реализации
Пример на Java 1.6: Без внутренних классов (ленивая несинхронизированная реализация).
Исходный текст на языке Javapublic class Singleton { private static Singleton instance; private Singleton (){ } public static Singleton getInstance(){ if (instance == null){ instance = new Singleton(); } return instance; } }
Пример на Java 1.5: с отложенной инициализацией
Исходный текст на языке Javaclass Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
Пример на Java 1.5: Class holder on JVM start initialization
Исходный текст на языке Javapublic class Singleton { private Singleton() {} private static class SingletonHolder { public static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } }
Пример на Java 1.5: Enum singleton
Исходный текст на языке Javapublic enum SingletonEnum { INSTANCE; public void someMethod() { *** } public void anotherMethod() { *** } }
Пример на Python
Исходный текст на языке Pythonclass Singleton(object): def __new__(cls): if not hasattr(cls, 'instance'): cls.instance = super(Singleton, cls).__new__(cls) return cls.instance >>> a = Singleton() >>> b = Singleton() >>> a is b True
Пример на C++
Возможная реализация на C++ (известная как синглтон Майерса), где одиночка представляет собой статический локальный объект. Важным моментом является то, что конструктор класса объявлен как private, что позволяет предотвратить создание экземпляров класса за пределами его реализации. Помимо этого, закрытыми также объявлены конструктор копирования и оператор присваивания. Последние следует определять, так как это позволяет в случае их случайного вызова из кода получить легко обнаруживаемую ошибку компоновки. Отметим также, что приведенный пример не является потокобезопасным в С++03, для работы с классом из нескольких потоков нужно защитить переменную
theSingleInstance
от одновременного доступа, например, с помощью мьютекса или критической секции. Впрочем, в C++11 синглтон Майерса является потокобезопасным и без всяких блокировок.Исходный текст на языке C++class OnlyOne { public: static const OnlyOne& Instance() { static OnlyOne theSingleInstance; return theSingleInstance; } private: OnlyOne(){}; OnlyOne(const OnlyOne& root); OnlyOne& operator=(const OnlyOne&); };
Еще один пример реализации одиночки на c++ с возможностью наследования для создания интерфейса, каркасом которого послужит, собственно, одиночка. Временем "жизни" единственного объекта удобно управлять, используя механизм подсчета ссылок.
Исходный текст на языке 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#
Исходный текст на языке C#Для отложенной инициализации 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() { } }
Пример на PHP 4
Исходный текст на языке PHP4<?php class Singleton { function __construct( $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
Для чего нужен Singleton (на пальцах)?
Исходный текст на языке PHP5<?php class Singleton { protected static $instance; // object 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 ( is_null(self::$instance) ) { self::$instance = new Singleton; } return self::$instance; } public function doAction() { /* ... */ } } Singleton::getInstance()->doAction(); // Применение ?>
Пример на Delphi
Для Delphi 2005 и выше подходит следующий пример (не потоко-безопасный):
Исходный текст на языке Delphitype 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); NewInstance := Instance; end;
Для более ранних версий следует переместить код класса в отдельный модуль, а объявление
Instance
заменить объявлением глобальной переменной в его секцииimplementation
(до Delphi 7 включительно секцииclass var
иstrict private
отсутствовали).Пример на языке Io
Исходный текст на языке IoSingleton := Object clone Singleton clone := Singleton
Пример на языке Ruby
Исходный текст на языке Rubyclass 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
Исходный текст на языке 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
Исходный текст на языке VB.NETModule 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
Исходный текст на языке Perlpackage Singleton; use strict; my $singleton; sub new { return $singleton ||= bless({}, __PACKAGE__); } 1; package main; my $a = new Singleton; my $b = new Singleton; print $a.' eq '.$b; # >> Singleton=HASH(0x1f3cf40) eq Singleton=HASH(0x1f3cf40) print $a eq $b; # >> 1
Пример на ActionScript 3
Исходный текст на языке ActionScriptВариант с приватным классом:
package { public class Singleton { private static const _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."); } } }
Преимущества варианта с приватным классом:
- При попытке использовать конструктор напрямую, ошибка будет выявлена компилятором сразу же.
- Создание объекта происходит по запросу.
Преимущества варианта с использованием исключения:
- Меньше кода.
Пример на CoffeeScript
Исходный текст на языке CoffeeScriptКлассический подход
class Singleton instance = undefined constructor : -> if instance? return instance else instance = @ # Код конструктора console.assert( new Singleton is new Singleton );
Подход, основанный на возможности доступа к функции из её тела.
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 с инкапсуляцией
Исходный текст на языке JavaScriptМетод, основанный на сокрытии переменных с помощью замыканий. В качестве бонуса - возможность объявлять приватные методы и свойства, которые будут доступны и конструктору и методам "класса".
var Singleton = new function() { var instance; // Приватные методы и свойства // ... // Конструктор function Singleton() { if ( !instance ) instance = this; else return instance; // Публичные свойства } // Публичные методы Singleton.prototype.test = function() {}; return Singleton; } console.assert( new Singleton === new Singleton );
Без использования сокрытия переменных есть простое решение, основанное на том, что функция Singleton является объектом. Минусом является возможность изменения св-ва __instance вне класса:
function Singleton() { if (!Singleton.__instance) Singleton.__instance = this; else return Singleton.__instance; // Код конструктора располагается после проверки } console.assert( new Singleton === new Singleton ); alert(object1===object2); // 'true' - переменные ссылаются на один и тот же объект alert(object1.__instance); // 'undefined', т.к. __instance является статическим свойством "класса" Singleton
Наиболее короткий вариант.
var Singleton = new function() { var single = this; // Код конструктора return function() { return single }; } console.log( new Singleton === new Singleton ); //true
MooTools классы с соответствующим плагином (Class.Singleton - метакласс).
var Singleton = new Class.Singleton({ initialize: function(){ // Код конструктора }, method1: function(){ // Динамический метод } }); console.assert( new Singleton === new Singleton );
Пример на Objective-C
Исходный текст на языке Objective-CSingleton.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
См. также
Ссылки
- Паттерн Singleton (Одиночка) — пример использования шаблона (C++).
- Одиночка — простое описание с примером применения.
- [1] — The «Double-Checked Locking is Broken» Declaration in java
- Реализация синглтонов на Perl — пример для Perl.
- Singleton Considered Stupid — критика Паттерна Singleton
- Мультисинглтон — Фабрика синглтонов.
- Паттерн Singleton (Одиночка) — три варианта реализации на C++.
- Классы-одиночки — реализация для Delphi 6/7.
Примечания
Шаблоны проектирования Основные Порождающие Абстрактная фабрика • Объектный пул • Одиночка • Отложенная инициализация • Прототип • Строитель • Фабричный метод
Структурные Поведенческие Интерпретатор • Итератор • Команда • Наблюдатель • Посетитель • Посредник • Состояние • Стратегия • Хранитель • Цепочка обязанностей • Шаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнение • Планировщик Категория:- Шаблоны проектирования
Wikimedia Foundation. 2010.