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']
            );
        }
    }