PHP
Что такое Сервис-Контейнер? Фреймворк-независимое определение
Привет всем!
Если вы следите за моим блогом, я упоминал где-то в статье Decorator Design Pattern термин Service Container. В этой записи я постараюсь максимально просто объяснить, что такое сервисный контейнер и как он используется. Может быть, мы сможем создать собственное доказательство концепции сервисного контейнера
Но подождите! Что я имею в виду под фреймворком? Я просто хочу сказать, что попытаюсь определить, что такое сервисный контейнер, без использования фреймворков. Хотя я использую популярные фреймворки, такие как Laravel и Symfony, в реальной жизни (как будто это не часть реальной жизни), я хочу изучить и понять базовую концепцию, используя простые термины и реализацию. Короче просто фреймворко-независимое определение (FAD) :-) .
Сервис и контейнер
Начнем с того, что есть сервисы. Сервисы - это просто классы, которые мы создаем и используем в нашем коде. Это может быть класс репозитория, служебный класс, класс регистратора - все, что вы вводите в другие классы. Даже классы контроллеров можно считать сервисами. Контейнеры определены в словаре - это объект, который можно использовать для хранения чего-либо. Итак, сервисные контейнеры - это объекты, содержащие сервисы. :)
Внедрение зависимости
Вы, наверное, задались вопросом, зачем нам использовать сервисные контейнеры? В основном это для внедрения зависимостей. Внедрение зависимостей означает, что зависимости классов вводятся или передаются через методы конструктора или установщика.
Учиться на примере
Представьте себе этот пример, в котором классы имеют «вложенные» зависимости. Перед сервисными контейнерами и внедрением зависимостей я делал что-то вроде этого:
<?php
class UserEmailSender implements UserEmailSenderInterface
{
private $repo;
private $logger;
public function __construct(UserRepositoryInterface $userRepo, LoggeerInterface $reader)
{
$this->repo = new UserRepository(new Connection());
$this->logger = new Logger();
}
// Imagine more methods below
}
<?php
class UserImporter implements UserImporterInterface
{
private $repo;
private $csvReader;
public function __construct(UserRepositoryInterface $userRepo, ReaderInterface $reader)
{
$this->repo = $userRepo;
$this->csvReader = $reader;
}
// Imagine more methods below
}
Теперь каждый раз, когда мы хотим использовать UserImporter или UserEmailSender, нам нужно создать экземпляры их зависимостей. Каждый раз, всегда! Вроде этого:
<?php
//Actions/Users/Import.php
// Представьте себе этот код в вашем основном файле импорта PHP
$userRepository = new UserRepository(new Connection());
$csvReader = new CsvReader();
$userImporter = new UserImporter($userRepository, $csvReader);
// Пример испльзования
$userImporter->import($_POST['csv_file']);
<?php
//Actions/Users/SendMail.php
// Представьте себе этот код в вашем php-файле для отправки почты
$userRepository = new UserRepository(new Connection());
$logger = new Logger();
$userEmailSender = new UserEmailSender($userRepository, $logger);
// Пример использования:
$userEmailSender->send($_POST['user'], $_POST['body']);
Отстой, правда? Я делал такие вещи на всех своих уроках давным-давно. Но благодаря фреймворкам, предоставляющим сервис-контейнер, мы можем вывести логику создания экземпляров зависимостей из всех классов.
Идея контейнера служб заключается в том, что мы создаем или настраиваем все наши службы и помещаем их в контейнер служб для дальнейшего использования.
Сервисный контейнер Simple Array
Итак, чтобы увидеть, как работает сервисный контейнер, давайте попробуем создать свой собственный в простейшем виде. Мы собираемся использовать для этого интерфейс PSR-11 . В этом простом примере мы все настроим и создадим экземпляры наших сервисов в конструкторе. В других фреймворках, таких как Laravel, настройка выполняется в поставщиках услуг, и эти поставщики услуг затем используются контейнером служб, который знает службы, которые вы хотите настроить. В этом случае нашим поставщиком услуг будет простой массив ;-)
<?php
use Psr\Container\ContainerInterface;
class SimpleArrayServiceContainer implements ContainerInterface
{
private $services = [];
public function __construct() {
$this->setupEverythingInArray();
}
public function get($id)
{
if(\isset($this->services[$id]) === false) {
throw ServiceNotFoundException();
}
return $this->services[$id];
}
public function has($id)
{
return \isset($this->services[$id]);
}
private function setupEverythingInArray()
{
// Setup utiliity classes
$this->services[LoggerInterface::class] = new Logger();
$this->services[UserRepositoryInterface::class] = new UserRepository(new Connection());
$this->services[ReaderInterface::class] = new CsvReader();
// Setup application classes
$this->services[UserImporterInterface::class] = new UserImporter($this->get(UserRepositoryInterface::class), $this->get(ReaderInterface::class));
$this->services[UserEmailSenderInterface::class] = new UserImporter($this->get(UserRepositoryInterface::class), $this->get(LoggerInterface::class));
}
}
Теперь, когда у нас есть сервисный контейнер, мы можем просто использовать его как единый источник истины, откуда будут поступать наши сервисы. Давайте посмотрим, как это будет выглядеть, используя его в наших классах действий.
<?php
// Imagine this code in your main import php file
$serviceContainer = new SimpleArrayServiceContainer();
$userImporter = $serviceContainer->get(UserImporterInterface::class);
// Just an example usage.
$userImporter->import($_POST['csv_file']);
Actions/Users/SendMail.php
<?php
// Imagine this code in your main send mail php file
$serviceContainer = new SimpleArrayServiceContainer();
$userEmailSender = $serviceContainer->get(UserEmailSenderInterface::class);
// Just an example usage.
$userEmailSender->send($_POST['user'], $_POST['body']);
Это еще не все
Наша реализация сервисного контейнера довольно проста и понятна, не так ли? Это простой пример того, как работает сервисный контейнер. Конечно, если вы знакомы с Laravel, у вас будет концепция автоматической привязки, которая просто использует Reflection и рекурсивно определяет, каковы зависимости каждого класса и многое другое.
Надеюсь, вам понравилось это «Framework-Agnostic Definition (FAD)» контейнера служб. Мы скоро рассмотрим и другие подобные вещи ;-)
Ура и счастливого кодирования! ;-)
https://unorderedlist.io/what-is-a-service-container/
Leave a reply