Приведи пример задачи, в которой нужен паттерн Singleton.

«Приведи пример задачи, в которой нужен паттерн Singleton.» — вопрос из категории Паттерны, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Пример: Логгер приложения В веб-приложении разные части системы (контроллеры, сервисы, middleware) должны писать логи в один и тот же файл или отправлять в одну и ту же внешнюю систему (например, Elasticsearch через Monolog). Создание нового экземпляра логгера для каждого вызова было бы избыточным и могло бы привести к конфликтам при записи в файл.

// app/Logging/AppLogger.php
class AppLogger
{
    private static ?self $instance = null;
    private MonologLogger $logger;

    // Приватный конструктор предотвращает создание через `new`
    private function __construct()
    {
        $this->logger = new MonologLogger('app');
        $handler = new StreamHandler(storage_path('logs/app.log'), Level::Debug);
        $this->logger->pushHandler($handler);
    }

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

    // Публичные методы для логирования
    public function info(string $message, array $context = []): void
    {
        $this->logger->info($message, $context);
    }

    public function error(string $message, array $context = []): void
    {
        $this->logger->error($message, $context);
    }
}

// Использование в любом месте приложения
// app/Services/PaymentService.php
class PaymentService
{
    public function process(Payment $payment): void
    {
        $logger = AppLogger::getInstance();

        try {
            // ... логика оплаты
            $logger->info('Payment processed', ['payment_id' => $payment->id]);
        } catch (Exception $e) {
            $logger->error('Payment failed', ['error' => $e->getMessage()]);
            throw $e;
        }
    }
}

Важно: В современных PHP-фреймворках (Laravel, Symfony) необходимость в «ручном» Singleton часто отпадает, так как контейнер внедрения зависимостей (DI Container) по умолчанию управляет сервисами в режиме singleton. Однако понимание паттерна важно для работы с legacy-кодом или при проектировании собственных библиотек.