2.7. Фасад (Facade)

2.7.1. Назначение

Основная цель паттерна Фасад заключается не в том, чтобы помешать вам прочитать инструкцию комплексной API. Это только побочный эффект. Главная цель всё же состоит в уменьшении связности кода и соблюдении Закона Деметры.

Фасад предназначен для разделения клиента и подсистемы путем внедрения многих (но иногда только одного) интерфейсов, и, конечно, уменьшения общей сложности.

  • Фасад не запрещает прямой доступ к подсистеме. Просто он делает его проще и понятнее.
  • Вы можете (и вам стоило бы) иметь несколько фасадов для одной подсистемы.

Вот почему хороший фасад не содержит созданий экземпляров классов (new) внутри. Если внутри фасада создаются объекты для реализации каждого метода, это не Фасад, это Строитель или [Абстрактная|Статическая|Простая] Фабрика [или Фабричный Метод].

Лучший фасад не содержит new или конструктора с type-hinted параметрами. Если вам необходимо создавать новые экземпляры классов, в таком случае лучше использовать Фабрику в качестве аргумента.

2.7.2. Диаграмма UML

Alt Facade UML Diagram

2.7.3. Код

Вы можете найти этот код на GitHub

Facade.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Facade;

class Facade
{
    /**
     * @var OperatingSystem
     */
    private $os;

    /**
     * @var Bios
     */
    private $bios;

    /**
     * @param Bios $bios
     * @param OperatingSystem   $os
     */
    public function __construct(Bios $bios, OperatingSystem $os)
    {
        $this->bios = $bios;
        $this->os = $os;
    }

    public function turnOn()
    {
        $this->bios->execute();
        $this->bios->waitForKeyPress();
        $this->bios->launch($this->os);
    }

    public function turnOff()
    {
        $this->os->halt();
        $this->bios->powerDown();
    }
}

OperatingSystem.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Facade;

interface OperatingSystem
{
    public function halt();

    public function getName(): string;
}

Bios.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Facade;

interface Bios
{
    public function execute();

    public function waitForKeyPress();

    public function launch(OperatingSystem $os);

    public function powerDown();
}

2.7.4. Тест

Tests/FacadeTest.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Facade\Tests;

use DesignPatterns\Structural\Facade\Facade;
use DesignPatterns\Structural\Facade\OperatingSystem;
use PHPUnit\Framework\TestCase;

class FacadeTest extends TestCase
{
    public function testComputerOn()
    {
        /** @var OperatingSystem|\PHPUnit_Framework_MockObject_MockObject $os */
        $os = $this->createMock('DesignPatterns\Structural\Facade\OperatingSystem');

        $os->method('getName')
            ->will($this->returnValue('Linux'));

        $bios = $this->getMockBuilder('DesignPatterns\Structural\Facade\Bios')
            ->setMethods(['launch', 'execute', 'waitForKeyPress'])
            ->disableAutoload()
            ->getMock();

        $bios->expects($this->once())
            ->method('launch')
            ->with($os);

        $facade = new Facade($bios, $os);

        // the facade interface is simple
        $facade->turnOn();

        // but you can also access the underlying components
        $this->assertSame('Linux', $os->getName());
    }
}