Singleton гарантує, що клас має один екземпляр, і дає до нього глобальний доступ:
final class Config
{
private static ?self $instance = null;
public static function getInstance(): self
{
return self::$instance ??= new self();
}
private function __construct() {}
}
Config::getInstance()->get('app.name');
Чому його вважають антипатерном (точніше - класичну реалізацію):
- прихована залежність: клас, що десь усередині викликає
Config::getInstance(), з конструктора не показує, що залежить від конфігурації; - глобальний стан: будь-який код може змінити спільний об'єкт - важко відстежити, хто й коли;
- тестування: статичний екземпляр не підміниш фейком без хитрощів, а стан «перетікає» між тестами;
- порушення SRP: клас відповідає і за свою роботу, і за керування власним життєвим циклом;
- довгоживучі процеси (Octane, черги): статичний стан живе між запитами - дані одного користувача можуть потрапити до іншого.
Задача «один екземпляр на застосунок» справжня - з'єднання з базою, клієнт API, конфігурація. Розв'язувати її краще контейнером:
// AppServiceProvider
$this->app->singleton(PaymentGateway::class, fn () => new StripeGateway(config('services.stripe.secret')));
// використання - звичайна залежність
final class CheckoutService
{
public function __construct(private PaymentGateway $payments) {}
}
- один екземпляр - контейнер створює об'єкт один раз і повертає його всім;
- явна залежність у конструкторі;
- підміна в тестах:
$this->app->instance(PaymentGateway::class, new FakeGateway)чиswap; - клас нічого не знає про те, що він «єдиний» - це рішення конфігурації, а не самого класу.
scoped() замість singleton() для Octane: екземпляр живе в межах одного запиту чи задачі в черзі і скидається між ними - захист від витоку стану між користувачами.
Фасади Laravel - не Singleton у класичному сенсі: це статичний доступ до об'єкта з контейнера, який можна підмінити (Cache::fake(), Mail::fake()). Але залежність через фасад так само прихована в коді методу, тож для бізнес-класів явне впровадження через конструктор зазвичай читабельніше.