Singleton гарантує, що клас має лише один екземпляр на весь застосунок, і дає до нього спільну точку доступу.
Яку проблему вирішує
Деякі ресурси мають існувати в єдиному екземплярі: реєстр, пул з'єднань, кешований конфіг. Створювати їх заново на кожну залежність - марнування ресурсів і ризик розсинхронізованого стану.
Як вирішує
У Laravel замість класичного Singleton зі static-властивістю та приватним конструктором використовують контейнерний singleton: app()->singleton(...). Контейнер створює об'єкт один раз і повертає той самий екземпляр на всі залежності.
class CurrencyRegistry
{
private array $rates = [];
public function set(string $code, float $rate): void
{
$this->rates[$code] = $rate;
}
public function get(string $code): float
{
return $this->rates[$code] ?? 1.0;
}
}
app()->singleton(CurrencyRegistry::class);
Чому контейнерний варіант кращий за класичний static: останній намертво "зашиває" залежність, його важко підмінити у тестах і неможливо мати дві різні конфігурації. Контейнерний singleton зберігає всі переваги DI - його легко замокати через app()->instance(...) або swap(), а клас отримує залежність через звичайний конструктор.
Де застосовувати
- Справді єдині на застосунок ресурси: реєстри, пул з'єднань, кешований конфіг.
- Для більшості сервісів singleton не потрібен - звичайне зв'язування і так передасть потрібну залежність через конструктор.
Плюси та мінуси
- + Економія ресурсів і єдиний централізований стан.
- + Контейнерний варіант лишається тестованим і підмінним.
- − Глобальний стан ускладнює тести й приховує залежності.
- − У довгоживучих процесах (Octane, queue worker) singleton зі станом запиту чи користувача спричинить витік даних між запитами - тримайте в singleton лише stateless або справді глобальний стан.