深入浅出之外观模式

494 阅读2分钟

外观模式

01 Facade定义

外观模式(又称门面模式) 是一种结构型设计模式,能为程序库、框架或其他复杂类提供一个简单的接口。

在业务实现中,我们经常会用到某各复杂库或框架内众多对象,同时,再加上业务逻辑,代码会变得庞大且难以维护。

如果应用外观模式,提供简单的接口,即可降低整个系统复杂度,为客户端减负。

02 需求场景

当你想看一场网络足球赛事,你需要打开路由器连接网络,以及电视接受信号,这时天气燥热,你可能还会打开空调纳凉。

整个操作较长,可能等你逐一操作完成,球赛已经过去了几分钟,其次,你需要熟悉每个设备操作方法,特定条件下,对你操作顺序也有要求,比如拉闸通电。 :(

如果有一个万能遥控器,一键操作上述电器开启/关闭,是不是很酷~ :)

03 实现“万能遥控器”

<?php
class Facade
{
    protected $router; //路由器
    protected $tv;     //电视
    protected $airconditioning //空调

    public function __construct(
        Router $router = null,
        TV $tv = null,
        AirConditioning $airC = null
    ) {
        $this->router = $router ?: new Router;
        $this->tv = $tv ?: new TV;
        $this->airC = $airC ?: new AirConditioning;
    }

    /**
     * 提供简单接口,实现开启操作
     */
    public function operationOn(): string
    {
        $result = "watch the game online:\n";
        $result .= $this->router->operationOn();
        $result .= $this->tv->operationOn();
        $result .= $this->airC->operationOn();

        return $result;
    }

    /**
     * 提供简单接口,实现关闭操作
     */
    public function operationOff(): string
    {
        $result = "End of the game,sleep:\n";
        $result .= $this->router->operationOff();
        $result .= $this->tv->operationOff();
        $result .= $this->airC->operationOff();

        return $result;
    }
}

class Router
{
    public function operationOn(): string
    {
        return "router: on!\n";
    }

    public function operationOff(): string
    {
        return "router: off!\n";
    }
}

class TV
{
    public function operationOn(): string
    {
        return "TV: on!\n";
    }

    public function operationOff(): string
    {
        return "TV: off!\n";
    }
}

class AirConditioning
{
    public function operationOn(): string
    {
        return "AirConditioning: on!\n";
    }

    public function operationOff(): string
    {
        return "AirConditioning: off!\n";
    }
}

function clientCode(Facade $facade)
{
    echo $facade->operationOn();
    echo $facade->operationOff();
}

$Router = new Router;
$TV = new TV;
$AirConditioning = new AirConditioning;
$facade = new Facade($Router, $TV, $Airconditioning);

//这里选用函数调用,也可以再客户端对象调用
clientCode($facade);
  • 客户端对象独立众多子类之外,子类升级,只需要修改facade中的子类
  • facade可能会随着复杂度提升,变得臃肿,那么我们可以新专用门面类

laravel中外观模式

Laravel中Router、File、Redis、Log等都有用到外观模式

01 框架应用

  • 引入Redis、DB门面类
  • 通过静态调用,获取数据

我们再laravel框架中,通过上述进行类调用,那么他们在底层如何运行的呢?

02 外观组件及加载

外观组件的配置数据,同 Laravel 其它服务一样被定义在 config/app.php 文件中。

  • 外观配置定义格式遵循 「别名」:「外观类」 的数据格式。

当一个 HTTP 请求被接收时,将在处理请求阶段将这些「外观」组件加载到服务中。

加载工作由定义在 Illuminate\Foundation\Http\Kernel 内核中的 \Illuminate\Foundation\Bootstrap\RegisterFacades::class 启动程序完成

  • 首先从配置文件 config/app.php 中读取所有外观服务配置 aliases;
  • 然后,读取别名服务$app->make(PackageManifest::class)->aliases();
  • 合并两项,注入到AliasLoader对象中去,完成注册

03 探秘Facade实现

我们以DB外观模式为例,这里只是简单的getFacadeAccessor()方法,返回"db"名

回到Facade基类中,我们发现并没有make方法,需要通过__callStatic魔术方法进行回调:

最终是通过Laravel的容器机制把DB的实际对象解析了出来。

04 Facade优势

复杂系统中,过多的子类和模块耦合,极大增加了系统复杂度和维护成本。

虽然整个框架实现facade增加了一定代码量,但是理解之后,框架脉络还是很清晰的,facade应用,也使得类的调用与框架优雅的解耦。

参考资料:

《深入浅出之laravel外观系统》柳公子