Одиночка (шаблон проектирования)
| Одиночка | |
| Singleton | |
| Тип: |
порождающий |
|---|---|
| Описан в Design Patterns |
Да |
Одиночка (англ. Singleton) в программировании — порождающий шаблон проектирования.
[править] Цель
Гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа. Существенно то, что можно пользоваться именно экземпляром класса, так как при этом во многих случаях становится доступной более широкая функциональность. Например, к описанным компонентам класса можно обращаться через интерфейс, если такая возможность поддерживается языком.
[править] Плюсы
- контролируемый доступ к единственному экземпляру;
- уменьшение числа имён;
- допускает уточнение операций и представления;
- допускает переменное число экземпляров;
- бо́льшая гибкость, чем у операций класса.
[править] Минусы
- Глобальные объекты могут быть вредны для объектного программирования, в некоторых случаях приводя к созданию немасштабируемого проекта.
- Усложняет написание модульных тестов и следованию TDD
[править] Применение
- должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам;
- единственный экземпляр должен расширяться путем порождения подклассов, и клиентам нужно иметь возможность работать с расширенным экземпляром без модификации своего кода.
[править] Пример реализации
[править] Пример на Java 1.5: с отложенной инициализацией
class Singleton { private volatile static 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
public class Singleton { private Singleton() {} private static class SingletonHolder { public static Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } }
[править] Пример на Java 1.5: Enum singleton
public enum SingletonEnum { INSTANCE; public void someMethod() { *** } public void anotherMethod() { *** } }
[править] Пример на Python
class Singleton(type): def __init__(cls, name, bases, dict): super(Singleton, cls).__init__(name, bases, dict) cls.instance = None def __call__(cls,*args,**kw): if cls.instance is None: cls.instance = super(Singleton, cls).__call__(*args, **kw) return cls.instance >>> class MyClass(object): ... __metaclass__ = Singleton ... >>> a = MyClass() >>> a.attr = 12 >>> b = MyClass() >>> b.attr 12 >>> a is b True
[править] Пример на C++
Возможная реализация на C++ (известная как синглтон Майерса), где одиночка представляет собой статический локальный объект. Важным моментом является то, что конструктор класса объявлен как private, что позволяет предотвратить создание экземпляров класса за пределами его реализации. Отметим также, что приведенный пример не является потокобезопасным, для работы с классом из нескольких потоков нужно защитить переменную theSingleInstance от одновременного доступа, например, с помощью мьютекса или критической секции.
class OnlyOne { public: static OnlyOne& const Instance() { static OnlyOne theSingleInstance; return theSingleInstance; } private: OnlyOne(){} OnlyOne(OnlyOne& root){} OnlyOne& operator=(OnlyOne&){} };
[править] Пример на 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
<?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 ) ) { $instance = new Singleton( false ); } return $instance; } } //usage $test = &Singleton::getInstance(); ?>
[править] Пример на PHP 5
<?php class Singleton { protected static $instance; // object instance /** * Защищаем от создания через new Singleton * * @return Singleton */ private function __construct() { /* ... */ } /** * Защищаем от создания через клонирование * * @return Singleton */ private function __clone() { /* ... */ } /** * Защищаем от создания через unserialize * * @return Singleton */ private function __wakeup() { /* ... */ } /** * Возвращает единственный экземпляр класса * * @return Singleton */ public static function getInstance() { if ( is_null(self::$instance) ) { self::$instance = new Singleton; } return self::$instance; } public function doAction() { /* ... */ } } //usage Singleton::getInstance()->doAction(); ?>
[править] Пример на 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); NewInstance := Instance; end;
Для более ранних версий следует переместить код класса в отдельный модуль, а объявление Instance заменить объявлением глобальной переменной в его секции implementation (до Delphi 7 включительно секции class var и strict private отсутствовали).
[править] Пример на языке 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
package Singleton; use strict; my $singleton; sub new { my $class = shift(); return $singleton ||= bless(sub {1}, $class); } 1;
[править] Пример на ActionScript 3
package { public class Singleton { public static const instance:Singleton= new Singleton; public function Singleton() { // Boolean(Singleton) is false when creating instance before static constructor executed. if(Singleton) throw new Error("Class is singleton."); } } }
[править] Пример на 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 = => @
ничего не даст, т.к. в результирующем JavaScript-коде constructor указывает на локальный конструктор Singleton, а не на класс Singleton.
Однако, если использовать пространства имён, то возможен такой вариант:
ns = {} class ns.Singleton constructor : -> # Код конструктора ns.Singleton = => @ console.assert( new ns.Singleton is new ns.Singleton )
[править] Пример на 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
Наиболее короткий вариант. Стоит отметить, что статические свойства "класса" затрутся.
function Singleton() { // Код конструктора var single = this; Singleton = function() { return single }; // Переопределяем конструктор } console.assert( new Singleton === new Singleton );
MooTools классы с соответствующим плагином (Class.Singleton - метакласс).
var Singleton = new Class.Singleton({ initialize: function(){ // Код конструктора }, method1: function(){ // Динамический метод } }); console.assert( 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
or
@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++).
- .ufna | Singleton для QObject — описание и пример реализации паттерна (C++, Qt)
- Одиночка — простое описание с примером применения.
- [1] — простой пример для Java.
- [2] — The «Double-Checked Locking is Broken» Declaration in java
- Реализация синглтонов на Perl — пример для Perl.
- Singleton Considered Stupid — критика Паттерна Singleton
- The Art Of Programming — Выпуск #9 — подкаст, с проcтым описанием этого паттерна (рус).
- Мультисинглтон — Фабрика синглтонов.
- Паттерн Singleton (Одиночка) — три варианта реализации на C++.
[править] Примечания
| Это заготовка статьи о компьютерных языках. Вы можете помочь проекту, исправив и дополнив её. |


