В этой серии мы рассмотрим творческие шаблоны проектирования:

  1. Одиночка
  2. Фабрика
  3. Абстрактная фабрика
  4. Опытный образец
  5. Строитель

❤️ Что это?

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

🫡 Почему…

Вы можете спросить себя: «С какой стати кому-то нужно контролировать, сколько экземпляров имеет класс?» Простой ответ — ограничить доступ к общим ресурсам, таким как базы данных.

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

🎨 Существует три распространенных типа одноэлементного паттерна:

  1. Нетерпеливая инициализация. В этом подходе экземпляр singleton создается во время загрузки класса, прежде чем какой-либо клиент сможет его запросить. Это гарантирует, что экземпляр синглтона всегда доступен, но может привести к ненужному использованию ресурсов, если синглтон не всегда требуется.
  2. Отложенная инициализация. При таком подходе экземпляр singleton создается только при первом запросе клиента. Это может уменьшить использование ресурсов, но также может вызвать проблемы с синхронизацией, если несколько потоков одновременно запрашивают синглтон.
  3. Блокировка с двойной проверкой: этот подход сочетает в себе преимущества предыдущих двух методов за счет использования отложенной инициализации и синхронизации. Одиночный экземпляр создается только при первом запросе, но процесс создания синхронизируется, чтобы гарантировать, что несколько потоков не создадут несколько экземпляров. Кроме того, синхронизация выполняется только во время первого запроса, поэтому последующие запросы не несут накладных расходов на синхронизацию.

🛞 Структура

Мы можем реализовать шаблон Singleton, создав класс с закрытым конструктором, закрытым статическим экземпляром класса и общедоступным статическим методом, который возвращает экземпляр. Частный конструктор гарантирует, что класс не может быть создан из-за пределов класса, а свойство закрытого статического экземпляра содержит единственный экземпляр класса. Наконец, общедоступный статический метод getInstance() используется для возврата одного экземпляра класса.

Нетерпеливая инициализация

class DatabaseSingleton {
  private static instance: DatabaseSingleton = new DatabaseSingleton();
  private constructor() {}
  public static getInstance(): DatabaseSingleton {
    return this.instance;
  }
  public query(sql: String): void {
    // ...
  }
}

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

class DatabaseSingleton {
  private instance: DatabaseSingleton | null = null;
  private constructor() {
    // ...
  }
  getInstance(): DatabaseSingleton {
    if (!instance) {
      instance = new DatabaseSingleton();
    }
    return instance;
  }  
}

Блокировка с двойной проверкой

class DatabaseSingleton {
  private static instance: DatabaseSingleton | null = null;
  private constructor() {}
  public static getInstance(): DatabaseSingleton {
   if (!this.instance) {
      synchronized(DatabaseSingleton) {
        if (!this.instance) {
          this.instance = new DatabaseSingleton();
        }
      }
    }
    return this.instance;
  }
}

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

class DatabaseSingleton {
  private instance?: DatabaseSingleton = null;
  private database: SQLDatabase = new SQLDatabase();  
  private constructor() {
    // ...
    database.connect();
    // ...
  }
  getInstance(): DatabaseSingleton {
    if (!instance) {
      instance = new DatabaseSingleton();
    }
    return instance;
  }

  insert<T>(record: T) {
    // ...
    database.insert<T>(recrod);
  }
}

В приведенном выше примере мы определяем класс с именем DatabaseSingleton с частным конструктором и частным статическим свойством с именем instance. Метод getInstance() проверяет, установлено ли уже свойство instance. Если он не установлен, он создает новый экземпляр класса и устанавливает свойство instance для этого экземпляра. Если свойство instance уже установлено, метод просто возвращает существующий экземпляр.

Вот пример использования класса Singleton:

const instance1 = DatabaseSingleton.getInstance();
const instance2 = DatabaseSingleton.getInstance();
console.log(instance1 === instance2); // true

В приведенном выше примере мы создаем два экземпляра класса Singleton, используя метод getInstance(). Поскольку шаблон Singleton ограничивает создание экземпляра класса одним экземпляром, и instance1, и instance2 на самом деле являются ссылками на один и тот же объект, и оператор console.log() выведет true.

const databaseSingleton = DatabaseSingleton.getInstance();
databaseSingleton.insert(...);

🤓 Итак, в заключение:

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

✅ Преимущества

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

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

❌ Недостатки

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

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



Надеюсь, вам понравилась статья ❤️