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