Что такое Singleton и как его реализовать?php-34

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

Основные характеристики Singleton

  1. Единственный экземпляр: Класс имеет только один экземпляр
  2. Глобальный доступ: Экземпляр доступен из любой части приложения
  3. Ленивая инициализация: Создается только при первом обращении

Классическая реализация на PHP

<?php
class DatabaseConnection
{
    private static $instance = null;
    private $connection;

    // Приватный конструктор запрещает прямое создание объекта
    private function __construct()
    {
        $this->connection = new PDO(
            'mysql:host=localhost;dbname=test',
            'username',
            'password'
        );
    }

    // Метод для получения экземпляра
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    // Запрещаем клонирование
    private function __clone() {}

    // Запрещаем десериализацию
    private function __wakeup() {}

    // Пример метода для работы с соединением
    public function query($sql)
    {
        return $this->connection->query($sql);
    }
}

// Использование
$db = DatabaseConnection::getInstance();
$results = $db->query('SELECT * FROM users');

Почему нужны приватные __clone и __wakeup

  1. __clone(): Предотвращает создание копии объекта через клонирование
  2. __wakeup(): Запрещает восстановление объекта при десериализации

Модификации Singleton

1. Потокобезопасная версия

public static function getInstance() {
    if (self::$instance === null) {
        static $lock = true;
        if ($lock) {
            $lock = false;
            self::$instance = new self();
        }
    }
    return self::$instance;
}

2. Singleton с параметрами

private function __construct($config) {
    $this->connection = new PDO(
        $config['dsn'],
        $config['username'],
        $config['password']
    );
}

public static function getInstance($config = null) {
    if (self::$instance === null && $config !== null) {
        self::$instance = new self($config);
    }
    return self::$instance;
}

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

  1. Контролируемый доступ: Единственная точка входа
  2. Экономия ресурсов: Не создает лишних экземпляров
  3. Глобальное состояние: Удобен для логгеров, конфигураций, подключений к БД

Критика и ограничения

  1. Глобальное состояние: Усложняет тестирование
  2. Нарушает SRP: Часто берет на себя несколько обязанностей
  3. Проблемы в многопоточности: Требует дополнительной синхронизации

Альтернативы

  1. Dependency Injection: Лучше для тестируемости
  2. Статические классы: Для чисто функциональных задач
  3. Service Locator: Когда нужно централизованное управление сервисами

Практический пример использования

class Logger
{
    private static $instance;
    private $logFile;

    private function __construct() {
        $this->logFile = fopen('app.log', 'a');
    }

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function log($message) {
        fwrite($this->logFile, date('Y-m-d H:i:s') . " - $message\n");
    }
}

// Использование в разных частях приложения
Logger::getInstance()->log('User logged in');
Logger::getInstance()->log('Data saved');

Резюмируем:

  • Singleton гарантирует единственный экземпляр класса
  • Реализуется через приватный конструктор и статический метод getInstance()
  • Полезен для ресурсоемких объектов (БД, логгеры)
  • Имеет ограничения и должен использоваться осознанно
  • Альтернативы (DI) часто предпочтительнее для тестируемого кода