dependency-injection Впровадження залежностей (англ. Dependency Injection, DI) - процес надання зовнішньої залежності програмному компоненту. Є специфічною формою “інверсії управління” (англ. Inversion of control, IoC), коли вона застосовується до управління залежностями.

Насправді цей шаблон ми вже використовували в наших прикладах. Ми оголошували залежність класу через типізацію аргументу конструктора:

 class MySuperClass
 {
     private $myClass;
    
     public function __construct(MyClass $class)
     {
         $this->myClass = $class;
     }
 }

Якщо Клас використовує певний набір параметрів, які можуть змінюватися, і його робота залежить від цих параметрів, вони повинні встановлюватися не в класі, а поза його межами. Таким чином, кожен раз при зміні параметрів налаштувань Ви не повинні лізти в код класу, щоб змінити їх.

Якщо подивитися в тест, ми побачимо, що ми тестуємо тільки клас Worker. Він залежить від інтерфейсу “BurgerInterface”. Якби ми захардкодили залежність прямо в конструкторі:

 public function __construct()
 {
     $this->myClass = new MyClass();
 }

Для тестування нам знадобиться:

  • реалізувати інтерфейс;
  • протестувати клас реалізації;
  • протестувати клас Worker.

Так як ми використовуємо DI, ми можемо просто замокати об’єкт типу BurgerInterface. Це дасть нам впевненість у працездатності саме нашого класу Worker, без будь-яких інших.

  • BurgerInterface.php
                  
    <?php
    
    namespace DesignPatterns\Structural\DependencyInjection;
    
    /**
     * Interface BurgerInterface
     * @package DesignPatterns\Structural\DependencyInjection
     */
    interface BurgerInterface
    {
        /**
         * @return array
         */
        public function getBurger(): array;
    }
    
                  
  • Worker.php
                  
    <?php
    
    namespace DesignPatterns\Structural\DependencyInjection;
    
    /**
     * Class Worker
     * @package DesignPatterns\Structural\DependencyInjection
     */
    class Worker
    {
        /**
         * @var BurgerInterface
         */
        protected $burger;
    
        /**
         * Worker constructor.
         *
         * @param BurgerInterface $burger
         */
        public function __construct(BurgerInterface $burger)
        {
            $this->burger = $burger;
        }
    
        /**
         * @return array
         */
        public function getBurger(): array
        {
            return $this->burger->getBurger();
        }
    }
    
                  
  • Test case:

    DependencyInjectionTest.php
      
    <?php
    namespace DesignPatterns\Tests\Structural\DependencyInjection;
    
    use DesignPatterns\Structural\DependencyInjection\Worker;
    
    class DependencyInjectionTest extends \PHPUnit_Framework_TestCase
    {
        protected $cheeseburgerReturnDataFixture = [
            'muffin'     => 'muffin',
            'meat'       => 'chicken',
            'sauce'      => 'mayonnaise',
            'withCheese' => true,
        ];
    
        protected $hamburgerReturnDataFixture = [
            'muffin'     => 'muffin',
            'meat'       => 'beef',
            'sauce'      => 'ketchup',
            'withCheese' => false,
        ];
    
        public function testWorkerMakeHamburger()
        {
            $mockHamburger = $this->createMock(
                'DesignPatterns\Structural\DependencyInjection\BurgerInterface'
            );
    
            $mockHamburger->expects($this->any())
                ->method('getBurger')
                ->will($this->returnValue($this->hamburgerReturnDataFixture));
    
            $worker = new Worker($mockHamburger);
            $burger = $worker->getBurger();
            $this->assertEquals($this->hamburgerReturnDataFixture, $burger);
        }
    
        public function testWorkerMakeCheeseburger()
        {
            $mockCheeseburger = $this->createMock(
                'DesignPatterns\Structural\DependencyInjection\BurgerInterface'
            );
    
            $mockCheeseburger->expects($this->any())
                ->method('getBurger')
                ->will($this->returnValue($this->cheeseburgerReturnDataFixture));
    
            $worker = new Worker($mockCheeseburger);
            $burger = $worker->getBurger();
            $this->assertEquals($this->cheeseburgerReturnDataFixture, $burger);
        }
    }