Как разработчики, мы всегда пытаемся найти новые способы написания хорошо спроектированного и чистого кода, внедряя новые стили, используя шаблоны проектирования и пробуя новые надежные фреймворки. В этой статье мы рассмотрим шаблон проектирования внедрения зависимостей через компонент IoC Laravel и посмотрим, как он может улучшить наш дизайн.
Внедрение зависимости
Внедрение зависимостей — это термин, придуманный Мартином Фаулером , и это процесс внедрения компонентов в ваше приложение. Как сказал Уорд Каннингем :
Внедрение зависимостей является ключевым элементом гибкой архитектуры.
давайте посмотрим на пример:
class UserProvider{ protected $connection; public function __construct(){ $this->connection = new Connection; } public function retrieveByCredentials( array $credentials ){ $user = $this->connection ->where( 'email', $credentials['email']) ->where( 'password', $credentials['password']) ->first(); return $user; } } 
  Если вы хотите протестировать или поддерживать этот класс, вам нужно получить доступ к реальной базе данных и выполнить несколько запросов.  Чтобы избежать необходимости делать это и отделить класс от остальных, у вас есть один из трех вариантов внедрения класса Connection без непосредственного его использования. 
При добавлении компонентов в ваш класс вы можете использовать один из трех вариантов:
Конструктор Инъекция
 class UserProvider{ protected $connection; public function __construct( Connection $con ){ $this->connection = $con; } ... 
Сеттер Инъекция
Точно так же мы можем внедрить нашу зависимость, используя метод установки:
 class UserProvider{ protected $connection; public function __construct(){ ... } public function setConnection( Connection $con ){ $this->connection = $con; } ... 
Интерфейс впрыска
 interface ConnectionInjector{ public function injectConnection( Connection $con ); } class UserProvider implements ConnectionInjector{ protected $connection; public function __construct(){ ... } public function injectConnection( Connection $con ){ $this->connection = $con; } } 
Когда класс реализует наш интерфейс, мы определяем метод injectConnection для разрешения зависимости.
преимущества
Теперь при тестировании нашего класса мы можем смоделировать класс зависимости и передать его в качестве параметра. Каждый класс должен быть ориентирован на конкретную задачу и не должен заниматься решением своих зависимостей. Таким образом, у вас будет более сфокусированное и обслуживаемое приложение.
Если вы хотите узнать больше о DI, Алехандро Джервассио подробно и профессионально освещал эту статью в этой серии , так что обязательно прочитайте эти статьи. А как насчет IoC? Ioc (Inversion of control) не требуется для использования внедрения зависимостей, но он может помочь вам эффективно управлять своими зависимостями.
Инверсия контроля
Ioc — это простой компонент, который делает разрешение зависимостей более удобным. Вы описываете свои объекты в контейнере, и каждый раз, когда вы разрешаете класс, зависимости вводятся автоматически.
Ларавел Иок
Laravel Ioc — это особенный способ разрешения зависимостей, когда вы запрашиваете объект:
  Мы будем использовать простой пример, который мы улучшим в этой статье. 
  Класс SimpleAuth имеет зависимость FileSessionStorage , поэтому наш код может выглядеть следующим образом: 
 class FileSessionStorage{ public function __construct(){ session_start(); } public function get( $key ){ return $_SESSION[$key]; } public function set( $key, $value ){ $_SESSION[$key] = $value; } } class SimpleAuth{ protected $session; public function __construct(){ $this->session = new FileSessionStorage; } } //creating a SimpleAuth $auth = new SimpleAuth(); 
Это классический способ сделать это, давайте начнем с использования инжектора конструктора.
 class SimpleAuth{ protected $session; public function __construct( FileSessionStorage $session ){ $this->session = $session; } } 
Теперь мы создаем наш объект:
 $auth = new SimpleAuth( new FileSessionStorage() ); 
Теперь я хочу использовать Laravel Ioc для управления всем этим.
  Поскольку класс Application расширяет класс Container , вы всегда можете получить доступ к контейнеру через фасад App . 
 App::bind( 'FileSessionStorage', function(){ return new FileSessionStorage; }); 
  Первый параметр для метода bind — это уникальный идентификатор для привязки к контейнеру, второй — функция обратного вызова, которая должна выполняться каждый раз, когда мы разрешаем класс FileSessionStorage , мы также можем передать строку, представляющую имя класса, как мы увидим далее. 
  Примечание: если вы проверяете пакеты Laravel, вы увидите, что иногда привязки группируются как ( view , view.finder ..). 
Допустим, что, возможно, мы хотим переключить наше хранилище сеансов на MySql, наш класс должен быть похож на:
 class MysqlSessionStorage{ public function __construct(){ //... } public function get($key){ // do something } public function set( $key, $value ){ // do something } } 
  Теперь, когда мы изменили зависимость, нам нужно изменить конструктор SimpleAuth и привязать новый объект к контейнеру! 
Модули высокого уровня не должны зависеть от модулей низкого уровня. И то и другое
должно зависеть от абстракций.
Абстракции не должны зависеть от деталей. Детали должны зависеть
на абстракции.Роберт С. Мартин
  Наш класс SimpleAuth не должен беспокоиться о том, как SimpleAuth наше хранилище, вместо этого он должен больше фокусироваться на потреблении сервиса. 
Итак, мы можем абстрагировать нашу реализацию хранилища:
 interface SessionStorage{ public function get( $key ); public function set( $key, $value ); } 
  Таким образом, мы можем просто реализовать и запросить экземпляр интерфейса SessionStorage : 
 class FileSessionStorage implements SessionStorage{ public function __construct(){ //... } public function get( $key ){ //... } public function set( $key, $value ){ //... } } class MysqlSessionStorage implements SessionStorage{ public function __construct(){ //... } public function get( $key ){ //... } public function set( $key, $value ){ //... } } class SimpleAuth{ protected $session; public function __construct( SessionStorage $session ){ $this->session = $session; } } 
  Если мы попытаемся разрешить класс SimpleAuth через контейнер с помощью App::make('SimpleAuth') , контейнер сгенерирует BindingResolutionException , после попытки разрешить класс из привязок, вернувшись к методу отражения и разрешив все зависимости , 
 Uncaught  exception 'Illuminate\Container\BindingResolutionException'   with  message 'Target [SessionStorage] is not instantiable.' 
Контейнер пытается создать экземпляр интерфейса. Мы можем исправить это, создав конкретную привязку для нашего интерфейса.
 App:bind( 'SessionStorage', 'MysqlSessionStorage' ); 
  Теперь каждый раз, когда мы пытаемся разрешить интерфейс через контейнер, мы получим экземпляр MysqlSessionStorage .  Если мы хотим переключить наш сервис хранения, мы можем просто обновить привязки. 
  Примечание: если вы хотите увидеть, связан ли класс с контейнером, вы можете использовать App::bound('ClassName') или App::bindIf('ClassName') чтобы зарегистрировать привязку, если она еще не была зарегистрировано. 
  Laravel Ioc также предлагает App::singleton('ClassName', 'resolver') для общих привязок. 
  Вы также можете использовать App::instance('ClassName', 'instance') для создания общего экземпляра. 
  Если контейнер не может разрешить зависимость, он сгенерирует ReflectionException , но мы можем использовать App::resolvingAny(Closure) чтобы разрешить любой данный тип или как форму запасного варианта. 
  Примечание: если вы зарегистрируете распознаватель для данного типа, будет также вызван метод resolvingAny , но возвращается значение из метода bind . 
Заключительные советы
-   Где хранить привязки: 
Если у вас небольшое приложение, вы можете использовать вашglobal/start.php, но если ваш проект становится больше, вы должны использовать поставщика услуг . -   Тестирование: 
Когда вы просто тестируете, вам нужно подумать об использованииphp artisan tinker, он очень мощный и может увеличить ваш рабочий процесс тестирования Laravel. -   API отражения: 
PHP Reflection API является очень мощным, и если вы хотите понять Laravel Ioc, вам необходимо ознакомиться с Reflection API, обязательно ознакомьтесь с этим руководством для получения дополнительной информации. 
Вывод
Как всегда, лучший способ узнать что-то — это изучить исходный код. Laravel Ioc — это всего лишь один файл, и вам не потребуется много времени, чтобы пройти через все функции. Хотите узнать больше о Laravel IoC или IoC в целом? Дайте нам знать!
