Синглтон — это творческий шаблон проектирования, который позволяет гарантировать, что класс имеет только один экземпляр, обеспечивая при этом глобальный доступ к этой точке.

Картинка саркастичная, не принимайте это на свой счёт, но характеристики совпадают с характеристиками нашего Синглтона, так что это беспроигрышный вариант🙂

Зачем нам это нужно

Все зависит от необходимости в нем, которая может быть любой из этих

  1. Допустим, вы устанавливаете соединение с базой данных, вам не нужно устанавливать несколько соединений, достаточно одного.
  2. То же самое с программой чтения файлов
  3. То же самое и с процессорами данных.

Проблемы

  1. Создать его с помощью обычного конструктора непросто, поскольку вызов конструктора должен всегда возвращать новый объект по замыслу.
  2. Как и глобальная переменная, шаблон Singleton позволяет получить доступ к некоторому объекту из любого места программы. Однако он также защищает этот экземпляр от перезаписи другим кодом.
  3. Вы не хотите, чтобы код, решающий проблему №1, был разбросан по всей вашей программе. Гораздо лучше иметь его в одном классе, особенно если от него уже зависит остальная часть вашего кода.

Решение

  1. Сделайте конструктор по умолчанию закрытым, чтобы другие объекты не могли использовать оператор new с классом Singleton.
  2. Создайте статический метод создания, который действует как конструктор. Внутри этот метод вызывает частный конструктор для создания объекта и сохраняет его в статическом поле. Все последующие вызовы этого метода возвращают кэшированный объект.
  3. Поскольку он статический, при каждом вызове статического метода не выполняется никаких действий и возвращается тот же объект.

Пик

Мы собираемся создать класс Singleton с методом, который может передавать экземпляр. Поскольку он статический, нам не нужно выполнять какую-либо инициализацию объекта.

Псевдокод

class Singleton {
  private static instance: Singleton;

  private constructor() {}

  public static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }

    return Singleton.instance;
  }

  public someMethod() {
    // ...
  }
}

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

  1. Использование — это когда абсолютно нет необходимости создавать экземпляр объекта и его функциональность не связана с динамической природой.

Используйте шаблон Singleton, если вам нужен более строгий контроль над глобальными переменными

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

Этапы реализации

  1. Добавьте в класс частное статическое поле для хранения экземпляра синглтона.
  2. Объявите общедоступный статический метод создания для получения экземпляра синглтона.
  3. Реализуйте «ленивую инициализацию» внутри статического метода. Он должен создать новый объект при первом вызове и поместить его в статическое поле. Метод должен всегда возвращать этот экземпляр при всех последующих вызовах.
  4. Сделайте конструктор класса закрытым. Статический метод класса по-прежнему сможет вызывать конструктор, но не другие объекты.
  5. Просмотрите клиентский код и замените все прямые вызовы конструктора синглтона вызовами его статического метода создания.
public class 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;
    }

    public void someMethod() {
    }
}
//synchronized helps to avoid multiple creations in Multithreaded environment

Минусы

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

Таким образом, пользователь должен использовать его правильно, чтобы не возникло неожиданных сценариев.