abstract-factory Абстрактна фабрика (англ. Abstract factory) — породжувальний шаблон проектування, що використовується для створення низки пов’язаних або залежних об’єктів без зазначення їхніх конкретних класів. Класи, що зазвичай створюються, прагнуть реалізувати один і той же інтерфейс. Клієнт абстрактної фабрики не дбає про те, як створюються ці об’єкти, він просто знає, за якими ознаками вони взаємопов’язані і як із ними поводитися.

Плюси:

  • Ізолює конкретні класи;
  • Спрощує заміну сімейств товарів;
  • Гарантує поєднання продуктів.

Застосування:

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

Оскільки наша закусочна швидко росте, ми повинні вміти готувати різні продукти. Проаналізувавши ринок, ми вирішили внести до меню “Комбо”. Наш кухар може готувати будь-які “Комбо”, головне дати йому рецепт. Отже, сьогодні у нас буде “Американський день”. У нас є на цей випадок рецепт UsaComboRecipe, в якому описано, які види продуктів входять в комбо. Даємо його кухареві - і все. Кухар сам зробить все, як зазначено в рецепті. Завтра ми матимемо “Європейський день”. І весь процес відрізнятиметься лише рецептом.

У цьому прикладі видно, що ми використовуємо нашого старого знайомого Фабричний метод. Реалізація будь-яких фабрик заснована на Фабричному методі.

  • Burgers
    • AbstractBurger.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Burgers;
      
      /**
       * Class AbstractBurger
       * @package DesignPatterns\Creational\AbstractFactory\Burgers
       */
      abstract class AbstractBurger
      {
          /**
           * @var string
           */
          protected $meat;
      
          /**
           * @var string
           */
          protected $sauce;
      
          /**
           * @var bool
           */
          protected $withCheese;
      }
      
                    
    • Cheeseburger.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Burgers;
      
      /**
       * Class Cheeseburger
       * @package DesignPatterns\Creational\AbstractFactory\Burgers
       */
      class Cheeseburger extends AbstractBurger
      {
          /**
           * @var string
           */
          protected $meat = 'chicken';
      
          /**
           * @var string
           */
          protected $sauce = 'mayonnaise';
      
          /**
           * @var bool
           */
          protected $withCheese = true;
      }
      
                    
    • Hamburger.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Burgers;
      
      /**
       * Class Hamburger
       * @package DesignPatterns\Creational\AbstractFactory\Burgers
       */
      class Hamburger extends AbstractBurger
      {
          /**
           * @var string
           */
          protected $meat = 'beef';
      
          /**
           * @var string
           */
          protected $sauce = 'ketchup';
      
          /**
           * @var bool
           */
          protected $withCheese = false;
      }
      
                    
  • Potatoes
    • AbstractPotato.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Potatoes;
      
      /**
       * Class AbstractPotato
       * @package DesignPatterns\Creational\AbstractFactory\Potatoes
       */
      abstract class AbstractPotato
      {
          /**
           * @var string
           */
          protected $shape;
      }
      
                    
    • FriesPotato.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Potatoes;
      
      /**
       * Class FriesPotato
       * @package DesignPatterns\Creational\AbstractFactory\Potatoes
       */
      class FriesPotato extends AbstractPotato
      {
          /**
           * @var string
           */
          protected $shape = 'strips';
      }
      
                    
    • RusticPotato.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Potatoes;
      
      /**
       * Class RusticPotato
       * @package DesignPatterns\Creational\AbstractFactory\Potatoes
       */
      class RusticPotato extends AbstractPotato
      {
          /**
           * @var string
           */
          protected $shape = 'strips';
      }
      
                    
  • Recipes
    • AbstractFactoryInterface.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Recipes;
      
      use DesignPatterns\Creational\AbstractFactory\Burgers\AbstractBurger;
      use DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato;
      use DesignPatterns\Creational\AbstractFactory\Waters\AbstractWater;
      
      /**
       * Interface AbstractFactoryInterface
       * @package DesignPatterns\Creational\AbstractFactory\Recipes
       */
      interface AbstractFactoryInterface
      {
          /**
           * @return AbstractBurger
           */
          public function makeBurger(): AbstractBurger;
      
          /**
           * @return AbstractPotato
           */
          public function makePotato(): AbstractPotato;
      
          /**
           * @return AbstractWater
           */
          public function makeWater(): AbstractWater;
      }
      
                    
    • EuropeComboRecipe.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Recipes;
      
      use DesignPatterns\Creational\AbstractFactory\Burgers\AbstractBurger;
      use DesignPatterns\Creational\AbstractFactory\Burgers\Cheeseburger;
      use DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato;
      use DesignPatterns\Creational\AbstractFactory\Potatoes\FriesPotato;
      use DesignPatterns\Creational\AbstractFactory\Waters\AbstractWater;
      use DesignPatterns\Creational\AbstractFactory\Waters\FantaWater;
      
      /**
       * Class EuropeComboRecipe
       * @package DesignPatterns\Creational\AbstractFactory\Recipes
       */
      class EuropeComboRecipe implements AbstractFactoryInterface
      {
      
          /**
           * @return AbstractBurger
           */
          public function makeBurger(): AbstractBurger
          {
              return new Cheeseburger();
          }
      
          /**
           * @return AbstractPotato
           */
          public function makePotato(): AbstractPotato
          {
              return new FriesPotato();
          }
      
          /**
           * @return AbstractWater
           */
          public function makeWater(): AbstractWater
          {
              return new FantaWater();
          }
      }
      
                    
    • UsaComboRecipe.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Recipes;
      
      use DesignPatterns\Creational\AbstractFactory\Burgers\AbstractBurger;
      use DesignPatterns\Creational\AbstractFactory\Burgers\Hamburger;
      use DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato;
      use DesignPatterns\Creational\AbstractFactory\Potatoes\FriesPotato;
      use DesignPatterns\Creational\AbstractFactory\Waters\AbstractWater;
      use DesignPatterns\Creational\AbstractFactory\Waters\CocaColaWater;
      
      /**
       * Class UsaComboRecipe
       * @package DesignPatterns\Creational\AbstractFactory\Recipes
       */
      class UsaComboRecipe implements AbstractFactoryInterface
      {
      
          /**
           * @return AbstractPotato
           */
          public function makePotato(): AbstractPotato
          {
              return new FriesPotato();
          }
      
          /**
           * @return AbstractWater
           */
          public function makeWater(): AbstractWater
          {
              return new CocaColaWater();
          }
      
          /**
           * @return AbstractBurger
           */
          public function makeBurger(): AbstractBurger
          {
              return new Hamburger();
          }
      }
      
                    
  • Waters
    • AbstractWater.php
                    
      <?php
      namespace DesignPatterns\Creational\AbstractFactory\Waters;
      
      /**
       * Class AbstractWater
       * @package DesignPatterns\Creational\AbstractFactory\Waters
       */
      abstract class AbstractWater
      {
          /**
           * @var string
           */
          protected $color;
      }
      
                    
    • CocaColaWater.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Waters;
      
      /**
       * Class CocaColaWater
       * @package DesignPatterns\Creational\AbstractFactory\Waters
       */
      class CocaColaWater extends AbstractWater
      {
          /**
           * @var string
           */
          protected $color = 'black';
      }
      
                    
    • FantaWater.php
                    
      <?php
      
      namespace DesignPatterns\Creational\AbstractFactory\Waters;
      
      /**
       * Class FantaWater
       * @package DesignPatterns\Creational\AbstractFactory\Waters
       */
      class FantaWater extends AbstractWater
      {
          /**
           * @var string
           */
          protected $color = 'yellow';
      }
      
                    
  • Chef.php
                  
    <?php
    
    namespace DesignPatterns\Creational\AbstractFactory;
    
    use DesignPatterns\Creational\AbstractFactory\Burgers\AbstractBurger;
    use DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato;
    use DesignPatterns\Creational\AbstractFactory\Recipes\AbstractFactoryInterface;
    use DesignPatterns\Creational\AbstractFactory\Waters\AbstractWater;
    
    /**
     * Class Chef
     * @package DesignPatterns\Creational\AbstractFactory
     */
    class Chef
    {
        /**
         * @var AbstractFactoryInterface
         */
        protected $comboRecipe;
    
        /**
         * Chef constructor.
         *
         * @param AbstractFactoryInterface $recipe
         */
        public function __construct(AbstractFactoryInterface $recipe)
        {
            $this->comboRecipe = $recipe;
        }
    
        /**
         * @return AbstractBurger
         */
        public function getBurger(): AbstractBurger
        {
            return $this->comboRecipe->makeBurger();
        }
    
        /**
         * @return AbstractPotato
         */
        public function getPotato(): AbstractPotato
        {
            return $this->comboRecipe->makePotato();
        }
    
        /**
         * @return AbstractWater
         */
        public function getWater(): AbstractWater
        {
            return $this->comboRecipe->makeWater();
        }
    
        /**
         * @return array
         */
        public function getCombo(): array
        {
            return [
                'Burger' => $this->getBurger(),
                'Potato' => $this->getPotato(),
                'Water'  => $this->getWater(),
            ];
        }
    }
    
                  
  • Test case:

    AbstractFactory.php
      
    <?php
    namespace DesignPatterns\Tests\Creational\AbstractFactory;
    
    use DesignPatterns\Creational\AbstractFactory\Chef;
    use DesignPatterns\Creational\AbstractFactory\Recipes\EuropeComboRecipe;
    use DesignPatterns\Creational\AbstractFactory\Recipes\UsaComboRecipe;
    use PHPUnit_Framework_TestCase;
    
    class AbstractBookFactoryTest extends PHPUnit_Framework_TestCase
    {
        public function testUsaComboRecipeInstanceOfAbstractFactory()
        {
            $usaComboRecipe = new UsaComboRecipe();
    
            $this->assertInstanceOf('DesignPatterns\Creational\AbstractFactory\Recipes\UsaComboRecipe', $usaComboRecipe);
        }
    
        public function testEuropeComboRecipeInstanceOfAbstractFactory()
        {
            $europeComboRecipe = new EuropeComboRecipe();
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Recipes\EuropeComboRecipe',
                $europeComboRecipe
            );
        }
    
        public function testBurgerOnUSADay()
        {
            $usaComboRecipe = new UsaComboRecipe();
            $chef = new Chef($usaComboRecipe);
    
            $usaBurger = $chef->getBurger();
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Burgers\AbstractBurger',
                $usaBurger
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Burgers\Hamburger',
                $usaBurger
            );
        }
    
        public function testPotatoOnUSADay()
        {
            $usaComboRecipe = new UsaComboRecipe();
            $chef = new Chef($usaComboRecipe);
    
            $usaPotato = $chef->getPotato();
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato',
                $usaPotato
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\FriesPotato',
                $usaPotato
            );
        }
    
        public function testWaterOnUSADay()
        {
            $usaComboRecipe = new UsaComboRecipe();
            $chef = new Chef($usaComboRecipe);
    
            $usaWater = $chef->getWater();
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Waters\AbstractWater',
                $usaWater
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Waters\CocaColaWater',
                $usaWater
            );
        }
    
        public function testComboOnUSADay()
        {
            $usaComboRecipe = new UsaComboRecipe();
            $chef = new Chef($usaComboRecipe);
    
            $usaCombo = $chef->getCombo();
    
            $this->assertCount(
                4,
                $usaCombo
            );
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato',
                $usaCombo['Burger']
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\FriesPotato',
                $usaCombo['Burger']
            );
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato',
                $usaCombo['Potato']
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\FriesPotato',
                $usaCombo['Potato']
            );
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Waters\AbstractWater',
                $usaCombo['Water']
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Waters\CocaColaWater',
                $usaCombo['Water']
            );
        }
    
        public function testBurgerOnEuropeDay()
        {
            $europeComboRecipe = new EuropeComboRecipe();
            $chef = new Chef($europeComboRecipe);
    
            $europeBurger = $chef->getBurger();
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Burgers\AbstractBurger',
                $europeBurger
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Burgers\Hamburger',
                $europeBurger
            );
        }
    
        public function testPotatoOnEuropeDay()
        {
            $europeComboRecipe = new EuropeComboRecipe();
            $chef = new Chef($europeComboRecipe);
    
            $europePotato = $chef->getPotato();
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato',
                $europePotato
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\FriesPotato',
                $europePotato
            );
        }
    
        public function testWaterOnEuropeDay()
        {
            $europeComboRecipe = new EuropeComboRecipe();
            $chef = new Chef($europeComboRecipe);
    
            $europeWater = $chef->getWater();
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Waters\AbstractWater',
                $europeWater
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Waters\CocaColaWater',
                $europeWater
            );
        }
    
        public function testComboOnEuropeDay()
        {
            $europeComboRecipe = new EuropeComboRecipe();
            $chef = new Chef($europeComboRecipe);
    
            $europeCombo = $chef->getCombo();
    
            $this->assertCount(
                4,
                $europeCombo
            );
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato',
                $europeCombo['Burger']
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\FriesPotato',
                $europeCombo['Burger']
            );
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\AbstractPotato',
                $europeCombo['Potato']
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Potatoes\FriesPotato',
                $europeCombo['Potato']
            );
    
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Waters\AbstractWater',
                $europeCombo['Water']
            );
            $this->assertInstanceOf(
                'DesignPatterns\Creational\AbstractFactory\Waters\CocaColaWater',
                $europeCombo['Water']
            );
        }
    }