Представьте себя владельцем острова в океане — местом, где вы можете наслаждаться спокойствием, творить и расслабляться вдали от городской суеты. Вам нравится иметь полный контроль над островом, принимать решения, которые определяют его будущее, и не делить его с кем-либо.

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

Что такое паттерн Одиночка?

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

Вот несколько примеров, зачем это надо:

  • Логгер: Когда вам нужно записывать логи в приложении, можно использовать паттерн Одиночка для создания единственного экземпляра логгера. Это гарантирует, что все логи будут записываться в один и тот же файл или отправляться на центральный сервер, и не возникнет конфликтов при записи из разных частей приложения.
  • Менеджер настроек: Если у вас есть класс, отвечающий за управление настройками приложения (например, настройки соединения с базой данных), вы можете реализовать его как Одиночку. Таким образом, вы будете иметь гарантию, что настройки доступны в единственном экземпляре и изменения, внесенные в одной части приложения, будут видны во всех остальных частях.
  • Кэширование: Паттерн Одиночка может быть полезен при реализации кэширования данных. Вы можете создать класс кэша, который будет хранить результаты вычислений или запросов к базе данных. Благодаря Одиночке вы будете иметь доступ к кэшу из любой части приложения и управлять его содержимым централизованно.
  • Менеджер соединений: Если ваше приложение работает с несколькими источниками данных (например, базами данных или внешними сервисами), вы можете использовать паттерн Одиночка для создания менеджера соединений. Это позволит вам централизованно управлять соединениями и гарантировать, что каждый источник данных использует только одно соединение.
  • Корзина товаров: В веб-приложениях электронной коммерции часто требуется иметь единственный экземпляр класса, отвечающего за управление корзиной товаров. Паттерн Одиночка позволяет создать такой класс, который будет отслеживать добавленные в корзину товары, их количество и другую связанную информацию. Таким образом, вы можете гарантировать, что только одна корзина товаров существует во всем приложении и что она доступна из любой части системы для работы с товарами, расчета общей суммы и оформления заказа.

Реализация паттерна Одиночка

Для реализации паттерна Одиночка (Singleton) вам понадобятся следующие шаги:

  1. Создайте приватное статическое поле в классе, которое будет содержать единственный экземпляр класса.
  2. Определите приватный конструктор класса, чтобы предотвратить создание экземпляров извне.
  3. Создайте статический публичный метод, который будет использоваться для получения экземпляра класса.
  4. Внутри метода проверьте, существует ли уже экземпляр класса. Если он не создан, создайте новый экземпляр и присвойте его статическому полю.
  5. Верните экземпляр класса из метода.

Для реализации паттерна Одиночка (Singleton) в JavaScript вы можете использовать следующий подход:

let instance = null;

class Singleton {
  constructor() {
    if (!instance) {
      instance = this;
    }
    return instance;
  }
  
  // Дополнительные методы класса
}

// Пример использования
const singletonInstance1 = new Singleton();
const singletonInstance2 = new Singleton();

console.log(singletonInstance1 === singletonInstance2); // true

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

Примеры реализации паттерна Одиночка

Пример простого логгера на JavaScript с использованием паттерна Одиночка:

class Logger {
  constructor() {
    if (Logger.instance) {
      return Logger.instance;
    }
    Logger.instance = this;
    this.logs = [];
    return this;
  }

  log(message) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      message: message,
    };
    this.logs.push(logEntry);
    console.log(`[${logEntry.timestamp}] ${logEntry.message}`);
  }

  printLogs() {
    this.logs.forEach((logEntry) => {
      console.log(`[${logEntry.timestamp}] ${logEntry.message}`);
    });
  }
}

// Пример использования
const logger1 = new Logger();
logger1.log("Сообщение 1");

const logger2 = new Logger();
logger2.log("Сообщение 2");

logger1.printLogs();

В конструкторе класса проверяется наличие экземпляра. Если экземпляр уже существует, возвращается ссылка на него, в противном случае сохраняется текущий экземпляр.

Метод log(message) добавляет запись лога в массив logs и выводит сообщение в консоль с указанием временной метки.

Метод printLogs() выводит все записи логов в консоль с указанием временной метки.

Пример использования создает два экземпляра класса Logger, но они фактически указывают на один и тот же объект. Вызов logger1.log(«Сообщение 1») добавляет запись в лог, а logger2.log(«Сообщение 2») также добавляет запись, но оба вызова будут отображаться при вызове logger1.printLogs().

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