Одиночка (Singleton) — шаблон проектирования

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

Пример задачи: реализация работы с БД. Создаётся единственный объект для подключения к базе и выполнения запросов, причём доступ к объекту должен иметься из любого места кода.

Шаблон состоит из следующих элементов:
  • Приватный конструктор — объявляется как приватный, для запрета на создание новых объектов данного класса;
  • Статическое свойство — для хранения объекта класса;
  • Статический метод — возвращает единственный объект класса. Метод часто именуют getInstance.

Пример использования шаблона на языке PHP. Тут определён класс DB с методом getInstance для получения объекта. Весь смысл шаблона заключается в том, чтобы имелся только один объект класса.
<?

final class DB
{
    /**
     * Свойство, которое хранит объект класса
     */
    private static $instance = null;

    /**
     * Создаёт объект класса
     * @return DB
     */
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Делаем конструктор приватным, чтобы его нельзя было вызывать за пределами определния класса. 
     * Для получения объекта класса нужно вызывать метод DB::getInstance()
     */
    private function __construct()
    {}

    /**
     * Делаем магический метод __clone приватным, чтобы нельзя было создать новые объекты класса, используя клонирование
     */
    private function __clone()
    {}

    /**
     * При срабатывании магического метода __wakeup (метод пробуждения, выполняется автоматически при десериализации 
     * через unserialize) порождаем ошибку с сообщением о невозможности создания данным образом объекта класса DB
     */
    public function __wakeup()
    {
        throw new Exception("Нельзя выполнить десериализацию объекта класса DB");
    }
}

?>

Скрипт, в котором выполняется проверка, что две переменные объекта ссылаются на один и тот же участок памяти. Проверка выполняется с помощью оператора ===, который для объектов сверяет адреса памяти.
<?
//Подключение класса DB для работа с БД
require_once(__DIR__.'/DB.class.php');

$DB1 = DB::getInstance();
$DB2 = DB::getInstance();

if ($DB1 === $DB2) {
    echo 'Переменные $DB1 и $DB2 ссылаются на один и тот же участок памяти';
} else {
    echo 'Переменные $DB1 и $DB2 ссылаются на РАЗНЫЕ участки памяти';
}

?>

Результатом работы скрипта будет вывод сообщения:
Переменные $DB1 и $DB2 ссылаются на один и тот же участок памяти