Шаблон синглтона - Singleton pattern

Диаграмма классов иллюстрирующей одноэлементный шаблона.

В программной инженерии , то шаблон одноточечно является шаблоном проектирования программного обеспечения , что ограничивает создание экземпляра в виде класса к одному «единым» , например. Это полезно, когда нужен ровно один объект для координации действий в системе.

Термин происходит от математической концепции синглтона .

Обзор

Одноэлементный шаблон проектирования - это один из двадцати трех хорошо известных шаблонов проектирования «Банда четырех», которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения с целью облегчения его реализации, изменения и т.д. тестировать и повторно использовать объекты.

Шаблон проектирования singleton решает проблемы, позволяя:

  • Убедитесь, что у класса есть только один экземпляр
  • Легко получить доступ к единственному экземпляру класса
  • Управляйте его созданием
  • Ограничить количество экземпляров
  • Доступ к глобальной переменной

Шаблон проектирования singleton описывает, как решать такие проблемы:

  • Скрыть конструкторы этого класса .
  • Определите общедоступную статическую операцию ( getInstance()), которая возвращает единственный экземпляр класса.

По сути, одноэлементный шаблон заставляет его отвечать за то, чтобы он был создан только один раз. Скрытый конструктор - объявленный privateили - protectedгарантирует, что класс никогда не может быть создан извне. Доступ к публичной статической операции можно получить, используя имя класса и имя операции, например Singleton.getInstance().

Общее использование

Ведение журнала - классический пример синглтона.

Критика

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

Более того, поскольку к синглтону можно получить доступ откуда угодно без использования каких-либо параметров, любые зависимости не будут сразу видны разработчикам. Следовательно, разработчикам необходимо знать «внутреннюю работу кода, чтобы правильно его использовать».

Синглтоны также нарушают принцип единственной ответственности , потому что они не только отвечают за обычную задачу одиночного объекта, но также должны гарантировать, что только один экземпляр создается; обратите внимание, что это основано на «классическом» определении синглтона, в котором он отвечает за обеспечение своей собственной уникальности, например, с помощью статического getInstance()метода.

Другой недостаток состоит в том, что синглтоны сложно тестировать, потому что они несут глобальное состояние на протяжении всей программы. В частности, потому что для модульного тестирования требуются слабосвязанные классы, чтобы изолировать то, что тестируется. Кроме того, когда определенный класс взаимодействует с синглтоном, этот класс и синглтон становятся тесно связанными , что делает невозможным тестирование класса самостоятельно без тестирования синглтона.

Реализации

Реализации одноэлементного шаблона должны:

  • Убедитесь, что когда-либо существует только один экземпляр класса singleton; а также
  • Обеспечьте глобальный доступ к этому экземпляру.

Обычно это делают:

Экземпляр обычно хранится как частная статическая переменная ; экземпляр создается при инициализации переменной, в какой-то момент до первого вызова статического метода.

Джава

Реализация синглтона в Java :

public class Coin {

    private static final int ADD_MORE_COIN = 10;
    private int coin;
    private static Coin instance = new Coin(); // eagerly loads the singleton

    private Coin(){
        // private to prevent anyone else from instantiating
    }

    public static Coin getInstance() {
        return instance;
    }

    public int getCoin() {
        return coin;
    }

    public void addMoreCoin() {
        coin += ADD_MORE_COIN;
    }

    public void deductCoin() {
        coin--;
    }
}
public final class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Ленивая инициализация

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

public final class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }

        return instance;
    }
}

Python

class Singleton:
    __instance = None

    def __new__(cls, *args):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls, *args)
        return cls.__instance

C ++

Начиная с C ++ 11 , инициализация статической локальной переменной является поточно-ориентированной и происходит после первого вызова. Это более безопасная альтернатива, чем статическая переменная пространства имен или класса.

class Singleton {
 public:
  static Singleton& GetInstance() {
    // Allocate with `new` in case Singleton is not trivially destructible.
    static Singleton* singleton = new Singleton();
    return *singleton;
  }

 private:
  Singleton() = default;

  // Delete copy/move so extra instances can't be created/moved.
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(Singleton&&) = delete;
};

C #

Реализация синглтона на C # :

public sealed class Singleton
{
    public static Singleton Instance { get; } = new Singleton();

    private Singleton() { }
}

В C # вы также можете использовать статические классы для создания синглтонов, где сам класс является синглтоном.

public static class Singleton
{
    public static MyOtherClass Instance { get; } = new MyOtherClass();
}

Единство

Синглтоны могут быть полезным инструментом при разработке с помощью Unity благодаря тому, как создаются экземпляры классов. Этот метод предпочтительнее скрытия конструктора , поскольку в Unity можно создать экземпляр объекта с помощью скрытого конструктора. Чтобы предотвратить перезапись экземпляра, необходимо выполнить проверку, чтобы убедиться, что экземпляр перезаписан null. Если он не равен нулю, GameObjectсценарий , содержащий вредоносный сценарий, должен быть уничтожен.

Если другие компоненты зависят от синглтона, порядок выполнения скрипта должен быть изменен, чтобы гарантировать, что компонент, определяющий синглтон, будет выполнен первым.

class Singleton : MonoBehaviour
{
    public static Singleton Instance { get; private set; }

    private void Awake()
    {
        if (Instance != null && Instance != this) {
            Destroy(this.gameObject);
        } else {
            Instance = this;
        }
    }
}

Обратите внимание, что также можно реализовать синглтон, удалив только скрипт-нарушитель, а не GameObjectсам его, вместо этого вызвав Destroy(this).

Дротик

Реализация синглтона в Dart :

class Singleton {
    Singleton._();
    static Singleton get instance => Singleton._();
}

PHP

Реализация синглтона в PHP :

class Singleton
{
    private static $instance = null;

    private function __construct() {}

    public static function getInstance(): self
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }
}

Котлин

Ключевое слово объекта Kotlin объявляет одноэлементный класс

object Coin {
    private var coin: Int = 0

    fun getCoin():Int {
        return coin
    }

    fun addCoin() {
        coin += 10
    }

    fun deductCoin() {
        coin--
    }
}

Паскаль

Поточно- реализация одноточечного в Pascal :

unit SingletonPattern;

interface

type
  TTest = class sealed
  strict private
    FCreationTime: TDateTime;
  public
    constructor Create;
    property CreationTime: TDateTime read FCreationTime;
  end;

function GetInstance: TTest;

implementation

uses
  SysUtils
  , SyncObjs
  ;

var
  FCriticalSection: TCriticalSection;
  FInstance: TTest;

function GetInstance: TTest;
begin
  FCriticalSection.Acquire;
  try
    if not Assigned(FInstance) then
      FInstance := TTest.Create;

    Result := FInstance;
  finally
    FCriticalSection.Release;
  end;
end;

constructor TTest.Create;
begin
  inherited Create;
  FCreationTime := Now;
end;

initialization
  FCriticalSection := TCriticalSection.Create;

finalization
  FreeAndNil(FCriticalSection);

end.

Использование:

procedure TForm3.btnCreateInstanceClick(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to 5 do
    ShowMessage(DateTimeToStr(GetInstance.CreationTime));
end;

Смотрите также

использованная литература

внешние ссылки