外观模式

目的

外观模式,也称为门面模式,其主要目的不是为了避免你必须阅读复杂的 API 手册,这只是顺带的事儿。它的主要目标是减少耦合,并遵循迪米特法则。

门面(Facade)的意思是通过置入大量接口(但有时候只有一个)来解耦客户端和子系统,当然,这种做法也是为了降低复杂性。

  • 门面并不禁止您访问子系统
  • 你可以(应该)为一个子系统设置多个门面

这就是为什么一个好的「门面」不存在 new 这种写法。如果每个方法都有多种创建方式,那么,它就不是外观模式,而是一个构造者模式,或者是抽象工厂/静态工厂/简单工厂模式中的一种。

最佳实践是,Facade 没有 new 关键字,也没有带接口类型提示参数的构造函数。如果你需要创建新的实例,请使用一个 Factory 作为参数。

UML 类图

外观模式类图

代码

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
<?php declare(strict_types = 1);

namespace DesignPatterns\Structural\Facade;

class Facade
{
private OperatingSystem $os;
private Bios $bios;

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();
}

测试

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
<?php declare(strict_types = 1);

namespace DesignPatterns\Structural\Facade\Tests;

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

class FacadeTest extends TestCase
{
public function testComputerOn()
{
$os = $this->createMock(OperatingSystem::class);

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

$bios = $this->createMock(Bios::class);

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

/** @noinspection PhpParamsInspection */
$facade = new Facade($bios, $os);
$facade->turnOn();

$this->assertSame('Linux', $os->getName());
}
}