Фасад (англ. Facade) - относиться к классу структурных шаблонов. Представляет собой унифицированный интерфейс вместо
набора интерфейсов некоторой подсистемы.
Фасад предназначен для разделения клиента и подсистемы. Фасад не запрещает прямой доступ к подсистеме. Он делает его проще и понятнее.
Сам шаблон очень похож на Строитель. Не стоит забывать, что Строитель порождающий, а Фасад - структурный.
Когда наш клиент делает заказ, он не хочет знать что, где, когда происходит. Он просто делает заказ у кассира(CashMan
).
А кассир уже знает, что делать с этими данными, и с какими частями системы взаимодействовать для достижения результата.
-
BurgerWorker.php
<?php namespace DesignPatterns\Structural\Facade; /** * Class BurgerWorker * @package DesignPatterns\Structural\Facade */ class BurgerWorker { /** * @var array */ protected $burgers = [ 'hamburger' => [ 'muffin' => 'muffin', 'meat' => 'beef', 'sauce' => 'ketchup', 'withCheese' => false, ], 'cheeseburger' => [ 'muffin' => 'muffin', 'meat' => 'chicken', 'sauce' => 'mayonnaise', 'withCheese' => true, ], ]; /** * @var array */ protected $prices = [ 'hamburger' => 12.5, 'cheeseburger' => 14.25, ]; /** * @param string $burgerName * * @return array */ public function make(string $burgerName): array { return $this->burgers[$burgerName]; } /** * @param string $burgerName * * @return float */ public function getPrice(string $burgerName): float { return $this->prices[$burgerName]; } }
-
CashBox.php
<?php namespace DesignPatterns\Structural\Facade; use Exception; /** * Class CashBox * @package DesignPatterns\Structural\Facade */ class CashBox { /** * @var float */ protected $cash; /** * CashBox constructor. * * @param float $cash */ public function __construct(float $cash) { $this->cash = $cash; } /** * @return float */ public function getCash(): float { return $this->cash; } /** * @param $orderPrice * @param $cash * * @return float * @throws Exception */ public function makeOrder($orderPrice, $cash): float { $change = $cash - $orderPrice; if ($change < 0) { throw new Exception('Need more money.'); } $this->cash += $cash - $change; return $change; } }
-
CashMan.php
<?php namespace DesignPatterns\Structural\Facade; use InvalidArgumentException; /** * Class CashMan * @package DesignPatterns\Structural\Facade */ class CashMan { /** * @var BurgerWorker */ protected $burgerWorker; /** * @var WaterWorker */ protected $waterWorker; /** * @var CashBox */ protected $cashBox; /** * @var array */ protected $order = []; /** * @var float */ protected $orderPrice = 0.0; /** * CashMan constructor. * * @param BurgerWorker $burgerWorker * @param WaterWorker $waterWorker * @param CashBox $cashBox */ public function __construct(BurgerWorker $burgerWorker, WaterWorker $waterWorker, CashBox $cashBox) { $this->burgerWorker = $burgerWorker; $this->waterWorker = $waterWorker; $this->cashBox = $cashBox; } /** * @param array $order * @param $cash * * @return array */ public function makeOrder(array $order, $cash) { if (!array_key_exists('waters', $order) && !array_key_exists('burgers', $order)) { throw new InvalidArgumentException(); } if (array_key_exists('waters', $order)) { $this->setProductsToOrderWithPrice($this->waterWorker, $order['waters']); } if (array_key_exists('burgers', $order)) { $this->setProductsToOrderWithPrice($this->burgerWorker, $order['burgers']); } $change = $this->cashBox->makeOrder($this->orderPrice, $cash); $fullOrder = [ 'order' => $this->order, 'price' => $this->orderPrice, 'change' => $change, ]; $this->resetResult(); return $fullOrder; } /** * @param WaterWorker|BurgerWorker $worker * @param array|string $products * * @return void */ protected function setProductsToOrderWithPrice($worker, $products) { if (is_array($products)) { foreach ($products as $product) { $this->setProductAndPrice($worker, $product); } } else { $this->setProductAndPrice($worker, $products); } } /** * @param WaterWorker|BurgerWorker $worker * @param string $product * * @return void */ protected function setProductAndPrice($worker, $product) { $this->order[] = $worker->make($product); $this->orderPrice += $worker->getPrice($product); } /** * @return void */ protected function resetResult() { $this->orderPrice = 0; $this->order = []; } }
-
WaterWorker.php
<?php namespace DesignPatterns\Structural\Facade; /** * Class WaterWorker * @package DesignPatterns\Structural\Facade */ class WaterWorker { /** * @var array */ protected $waters = [ 'cocaCola' => 'Coca-Cola', 'fanta' => 'Fanta', 'sprite' => 'Sprite', ]; /** * @var array */ protected $prices = [ 'cocaCola' => 2, 'fanta' => 3, 'sprite' => 1, ]; /** * @param string $waterName * * @return string */ public function make(string $waterName): string { return $this->waters[$waterName]; } /** * @param string $waterName * * @return float */ public function getPrice(string $waterName): float { return $this->prices[$waterName]; } }
Test case:
FacadeTest.php
<?php
namespace DesignPatterns\Structural\Facade;
class FacadeTest extends \PHPUnit_Framework_TestCase
{
const MONEY_IN_CASH_BOX = 1000;
protected $firstOrder = [
'burgers' => 'hamburger',
'waters' => 'fanta',
'cash' => 30,
];
protected $secondOrder = [
'burgers' => [
'hamburger',
'cheeseburger',
],
'waters' => 'cocaCola',
'cash' => 50,
];
/**
* @var CashBox
*/
protected $cashBox;
/**
* @var CashMan
*/
protected $cashMan;
/**
* @var BurgerWorker
*/
protected $burgerWorker;
/**
* @var WaterWorker
*/
protected $waterWorker;
public function setUp(): void
{
$this->cashBox = new CashBox(self::MONEY_IN_CASH_BOX);
$this->burgerWorker = new BurgerWorker();
$this->waterWorker = new WaterWorker();
$this->cashMan = new CashMan(
$this->burgerWorker,
$this->waterWorker,
$this->cashBox
);
}
public function testMakeFirstOrder()
{
$this->assertEquals($this->cashBox->getCash(), self::MONEY_IN_CASH_BOX);
$firstOrder = $this->cashMan->makeOrder($this->firstOrder, $this->firstOrder['cash']);
$this->assertIsArray($firstOrder);
$this->assertArrayHasKey('price', $firstOrder);
$this->assertArrayHasKey('change', $firstOrder);
$this->assertEquals($firstOrder['change'], $this->firstOrder['cash'] - $firstOrder['price']);
$this->assertEquals($this->cashBox->getCash(), self::MONEY_IN_CASH_BOX + $firstOrder['price']);
return $firstOrder;
}
public function testMakeSecondOrder()
{
$secondOrder = $this->cashMan->makeOrder($this->secondOrder, $this->secondOrder['cash']);
$this->assertIsArray($secondOrder);
$this->assertArrayHasKey('price', $secondOrder);
$this->assertArrayHasKey('change', $secondOrder);
$this->assertEquals($secondOrder['change'], $this->secondOrder['cash'] - $secondOrder['price']);
$this->assertEquals(
$this->cashBox->getCash(),
self::MONEY_IN_CASH_BOX + $secondOrder['price']
);
}
}