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

Материал из Википедии — свободной энциклопедии
(перенаправлено с «Singleton»)
Перейти к: навигация, поиск
Шаблон проектирования
Одиночка
Singleton
Тип:

порождающий

Описан в Design Patterns

Да

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

Содержание

[править] Цель

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

[править] Плюсы

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

[править] Минусы

  • Глобальные объекты могут быть вредны для объектного программирования, в некоторых случаях приводя к созданию немасштабируемого проекта.
  • Усложняет написание модульных тестов и следованию TDD

[править] Применение

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

[править] Пример реализации

[править] Пример на 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

[править] См. также

[править] Ссылки

[править] Примечания


Личные инструменты
Пространства имён
Варианты
Действия
Навигация
Участие
Печать/экспорт
Инструменты
На других языках