factory-method Фабричний метод (англ. Factory Method, також відомий як Віртуальний конструктор (англ. Virtual Constructor)) — шаблон проєктування, що породжує, що надає підкласам інтерфейс для створення екземплярів деякого класу. На момент створення спадкоємці можуть визначити, який клас створювати. Іншими словами, Фабрика делегує створення об’єктів спадкоємцям батьківського класу. Це дозволяє використовувати в коді програми не специфічні класи, а маніпулювати абстрактними об’єктами на більш високому рівні.

Клас ** Burger , абстрактний клас. Дивлячись на нього, ми можемо зрозуміти, що має собою представляти бургер. І що всі бургери мають бути схожі на нього. Створимо два успадковані класи від “Burger” з деякими особливостями. Отримали **Hamburger та Cheeseburger. На нашій кухні є кухар(new Chef()) реалізує інтерфейс FactoryMethodInterface, отже він вміє робити бургери. Кожен кухар може створювати бургери по-різному, але на виході ми завжди отримаємо ** Burger . Щоб кухар приготував нам бургер, ми викликаємо його метод (Chef::makeBurger()**) і передаємо назву бургера. Кухар створює потрібний нам бургер чи каже, що такий бургер він готувати не вміє.

  • Burgers
    • Burger.php
                    
      <?php
      
      namespace DesignPatterns\Creational\FactoryMethod\Burgers;
      
      /**
       * Class Burger
       * @package DesignPatterns\Creational\FactoryMethod\Burgers
       */
      abstract class Burger
      {
          /**
           * @var string
           */
          protected $meat;
      
          /**
           * @var string
           */
          protected $sauce;
      
          /**
           * @var bool
           */
          protected $withCheese;
      
          /**
           * @return string
           */
          public function getMeat(): string
          {
              return $this->meat;
          }
      
          /**
           * @param string $meat
           *
           * @return void
           */
          public function setMeat(string $meat)
          {
              $this->meat = $meat;
          }
      
          /**
           * @return string
           */
          public function getSauce(): string
          {
              return $this->sauce;
          }
      
          /**
           * @param string $sauce
           *
           * @return void
           */
          public function setSauce(string $sauce)
          {
              $this->sauce = $sauce;
          }
      
          /**
           * @return bool
           */
          public function getWithCheese(): bool
          {
              return $this->withCheese;
          }
      
          /**
           * @param bool $withCheese
           *
           * @return void
           */
          public function setWithCheese(bool $withCheese)
          {
              $this->withCheese = $withCheese;
          }
      }
      
                    
    • Cheeseburger.php
                    
      <?php
      
      namespace DesignPatterns\Creational\FactoryMethod\Burgers;
      
      /**
       * Class Cheeseburger
       * @package DesignPatterns\Creational\FactoryMethod\Burgers
       */
      class Cheeseburger extends Burger
      {
          /**
           * @var string
           */
          protected $meat = 'chicken';
      
          /**
           * @var string
           */
          protected $sauce = 'mayonnaise';
      
          /**
           * @var bool
           */
          protected $withCheese = true;
      }
      
                    
    • Hamburger.php
                    
      <?php
      
      namespace DesignPatterns\Creational\FactoryMethod\Burgers;
      
      /**
       * Class Hamburger
       * @package DesignPatterns\Creational\FactoryMethod\Burgers
       */
      class Hamburger extends Burger
      {
          /**
           * @var string
           */
          protected $meat = 'beef';
      
          /**
           * @var string
           */
          protected $sauce = 'ketchup';
      
          /**
           * @var bool
           */
          protected $withCheese = false;
      }
      
                    
  • Chef.php
                  
    <?php
    
    namespace DesignPatterns\Creational\FactoryMethod;
    
    use DesignPatterns\Creational\FactoryMethod\Burgers\Burger;
    use DesignPatterns\Creational\FactoryMethod\Burgers\Hamburger;
    use DesignPatterns\Creational\FactoryMethod\Burgers\Cheeseburger;
    
    /**
     * Class Chef
     * @package DesignPatterns\Creational\FactoryMethod
     */
    class Chef implements FactoryMethodInterface
    {
        /**
         * @param string $typeBurger
         *
         * @return Burger
         */
        public function makeBurger(string $typeBurger): Burger
        {
            $typeBurger = strtolower($typeBurger);
    
            switch ($typeBurger) {
                case 'cheeseburger':
                    return new Cheeseburger();
                case 'hamburger':
                    return new Hamburger();
                default:
                    throw new \InvalidArgumentException('Sorry. But we haven\'t this burger.');
            }
        }
    }
    
                  
  • FactoryMethodInterface.php
                  
    <?php
    
    namespace DesignPatterns\Creational\FactoryMethod;
    
    use DesignPatterns\Creational\FactoryMethod\Burgers\Burger;
    
    /**
     * Interface FactoryMethodInterface
     * @package DesignPatterns\Creational\FactoryMethod
     */
    interface FactoryMethodInterface
    {
        /**
         * @param string $burgerType
         *
         * @return Burger
         */
        public function makeBurger(string $burgerType): Burger;
    }
    
                  
  • Test case:

    FactoryMethodTest.php
      
    <?php
    
    namespace DesignPatterns\Tests\Creational\FactoryMethod;
    
    use DesignPatterns\Creational\FactoryMethod\Chef;
    use InvalidArgumentException;
    use PHPUnit_Framework_TestCase;
    
    class FactoryMethodTest extends PHPUnit_Framework_TestCase
    {
        public function testWorkerInstanceOfFactoryMethodInterface()
        {
            $chef = new Chef();
    
            $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\FactoryMethodInterface', $chef);
        }
    
        public function testHamburgerInstanceOfBurger()
        {
            $chef = new Chef();
    
            $hamburger = $chef->makeBurger('hamburger');
            $someHamburger = $chef->makeBurger('hamburger');
    
            $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\Burgers\Burger', $hamburger);
            $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\Burgers\Burger', $someHamburger);
            $this->assertNotSame($hamburger, $someHamburger);
        }
    
        public function testCheeseburgerInstanceOfBurger()
        {
            $chef = new Chef();
    
            $cheeseburger = $chef->makeBurger('cheeseburger');
            $someCheeseburger = $chef->makeBurger('cheeseburger');
    
            $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\Burgers\Burger', $cheeseburger);
            $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\Burgers\Burger', $someCheeseburger);
            $this->assertNotSame($cheeseburger, $someCheeseburger);
        }
    
        public function testException()
        {
            $this->expectException(InvalidArgumentException::class);
            $chef = new Chef();
    
            $chef->makeBurger('someNameForBurger');
        }
    
        public function testHamburgerСomposition()
        {
            $chef = new Chef();
    
            $hamburger = $chef->makeBurger('hamburger');
    
            $hamburgerMeat = $hamburger->getMeat();
            $hamburgerSauce = $hamburger->getSauce();
            $hamburgerWithCheese = $hamburger->getWithCheese();
    
            $this->assertEquals('beef', $hamburgerMeat);
            $this->assertNotEquals('chicken', $hamburgerMeat);
    
            $this->assertEquals('ketchup', $hamburgerSauce);
            $this->assertNotEquals('mayonnaise', $hamburgerSauce);
    
            $this->assertEquals(false, $hamburgerWithCheese);
            $this->assertNotEquals(true, $hamburgerWithCheese);
        }
    
        public function testCheeseburgerСomposition()
        {
            $chef = new Chef();
    
            $cheeseburger = $chef->makeBurger('cheeseburger');
    
            $cheeseburgerMeat = $cheeseburger->getMeat();
            $cheeseburgerSauce = $cheeseburger->getSauce();
            $cheeseburgerWithCheese = $cheeseburger->getWithCheese();
    
            $this->assertEquals('chicken', $cheeseburgerMeat);
            $this->assertNotEquals('beef', $cheeseburgerMeat);
    
            $this->assertEquals('mayonnaise', $cheeseburgerSauce);
            $this->assertNotEquals('ketchup', $cheeseburgerSauce);
    
            $this->assertEquals(true, $cheeseburgerWithCheese);
            $this->assertNotEquals(false, $cheeseburgerWithCheese);
        }
    }