Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чому Singleton часто називають антипатерном і як контейнер Laravel розв'язує ту саму задачу?

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()). Але залежність через фасад так само прихована в коді методу, тож для бізнес-класів явне впровадження через конструктор зазвичай читабельніше.

Докладніше в документації: Refactoring.Guru: Singleton

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання