Также известен как Singleton

Одиночка

Суть паттерна

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

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

Проблема

Одиночка решает сразу две проблемы (нарушая принцип единственной ответственности):

  1. Гарантирует единственный экземпляр класса. Чаще всего это полезно для доступа к какому-то общему ресурсу, например базе данных.

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

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

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

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

Решение

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

Если у вас есть доступ к классу-одиночке, значит, будет доступ и к этому статическому методу. Из какой точки кода вы бы его не вызвали, он всегда будет отдавать один и тот же объект.

Аналогия из жизни

Правительство

Правительство государства — хороший пример одиночки. В государстве может быть только одно официальное правительство. Вне зависимости от того, кто конкретно заседает в правительстве, оно имеет глобальную точку доступа «Правительство страны N».

Структура

Схема структуры классов паттерна Одиночка
  1. Одиночка определяет статический метод getInstance(), который возвращает единственный экземпляр класса Одиночки.

    Конструктор одиночки должен быть скрыт от клиентов. Вызов getInstance() должен быть единственным способом получить объект этого класса.

Псевдокод

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

Паттерн Одиночка позволяет клиенту не беспокоиться о получении одного-единственного экземпляра объекта класса. Он предоставляет глобальную точку доступа к этому экземпляру — статический метод getInstance.

class Database is
    private field instance: Database

    static method getInstance() is
        if (this.instance == null) then
            acquireThreadLock() and then
                if (this.instance == null) then
                    this.instance = new Database()
        return this.instance

    private method Database() is
        // Здесь может жить код инициализации подключения к серверу баз данных.
        // ...

    public method query(sql) is
        // Все запросы к базе данных будут проходить через этот метод. Поэтому
        // имеет смысл поместить сюда какую-то логику кеширования.
        // ...

class Application is
    method main() is
        Database foo = Database.getInstance()
        foo.query("SELECT ...")
        // ...
        Database bar = Database.getInstance()
        bar.query("SELECT ...")
        // В переменной bar содержится тот же объект, что и в foo.

Применимость

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

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

Вам хочется иметь больше контроля над глобальными переменными.

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

Тем не менее в любой момент вы можете расширить это ограничение и позволить любое количество объектов-Одиночек, поменяв код в одном месте (метод getInstance()).

Шаги реализации

  1. Добавьте в класс приватное статическое поле, которое будет содержать одиночный объект.

  2. Объявите создающий метод — публичный статический метод — который будет использоваться для получения одиночки.

  3. Добавьте «ленивую инициализацию» (создание объекта при первом вызове метода) в создающий метод одиночки.

  4. Сделайте конструктор класса приватным.

  5. В клиентском коде замените вызовы конструктора вызовами создающего метода.

Преимущества и недостатки

  • Позволяет отложенную инициализацию.
  • Нарушает принцип единственной обязанности класса.
  • Маскирует плохой дизайн.
  • Проблемы мультипоточности.
  • Проблемы юнит-тестирования.

Отношения с другими паттернами

  • Фасад можно сделать Одиночкой, так как обычно нужен только один объект-фасад.

  • Паттерн Легковес может напоминать Одиночку, если для конкретной задачи у вас получилось уменьшить количество объектов к одному. Но помните, что между паттернами есть два кардинальных отличия:

    1. Объекты-легковесы должны быть неизменяемыми, тогда как объект-одиночка допускает изменение своего состояния.
    2. Вы можете иметь множество объектов легковесов одного класса, в отличие от одиночки, который требует наличия только одного объекта.
  • Абстрактная фабрика, Строитель и Прототип могут быть реализованы при помощи Одиночки.

Реализация в различных языках программирования

Java