Абстрактная фабрика (англ. 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']
);
}
}