Laravel 核心--深入剖析 Laravel 框架門面模式

yefy發表於2020-12-25

門面模式

門面模式 又叫 外觀模式,提供一個統一的介面去訪問多個子系統的多個不同的介面,它為子系統中的一組介面提供一個統一的高層介面,使得子系統更容易使用。

本質:就是化零為整;引入一箇中介類,把各個分散的功能組合成一個整體,只對外暴露一個統一的介面;

這兩年流行微服務,即化整為零,把一個大服務拆分成一個個零部件;而門面模式則是反其道,是化零為整;

目的

為了使用者使用方便,把過度拆分的分散功能,組合成一個整體,對外提供一個統一的介面

Laravel 框架中的門面模式

如何使用

Laravel 框架通過使用門面設計模式,使得可以通過呼叫類的靜態方法的方式去呼叫類的普通方法,十分的方便和簡單,以 Cache 類為例:

public function getCacheData($name)
{
    return Cache::get($name)
}

上述程式碼,其實呼叫的是 Cache 類的非靜態 get 方法

原始碼解析

這是 Illuminate\Support\Facades\Cache facade 類的原始碼:

class Cache extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'cache';
    }
}

我們是如何使用這個類的呢?

public function test()
{
   Cache::get('test');
}

上述程式碼看起來非常像呼叫 Cache 中的靜態方法 get ,但是我們很清楚的看到這個類中只有 getFacadeAccessor 這一個方法,並沒有靜態方法 get ,這裡的方法 get() 實際上存在於容器內部的服務中。所有的細節都隱藏在基本 Facade 類中。

當我們在 Cache 外觀上引用任何靜態方法時,Laravel 會解析 cache 服務容器中的 繫結並針對該物件執行請求的方法。

現在讓我們仔細研究一下這個細節,每個外觀都將擴充套件基本抽象 Facade 類。細節隱藏在這裡的三種方法中:

  • __callStatic() -簡單的PHP魔術方法
  • getFacadeRoot() -從IoC容器中獲取服務
  • resolveFacadeInstance() -負責解決服務例項

當我們呼叫靜態方法 get 的時候,Cache 類中沒有此方法,會自動呼叫父類中的魔術方法如下:

    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }

方法 getFacadeRoot() 返回外觀背後的服務物件的例項:

    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

方法 resolveFacadeInstance 複雜解決服務例項,在這裡,我們檢查傳遞給物件的引數,然後檢查是否已經解決了該服務。如果不是,則只需從容器中檢索它:

    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        if (static::$app) {
            return static::$resolvedInstance[$name] = static::$app[$name];
        }
    }

上述程式碼中 static::$app[$name] 值得注意的是,其中 $app 為應用容器,是一個物件,為什麼可以以陣列的方式訪問變數呢?那是因為容器實現了 ArrayAccess 類,這個類使得容器物件有了訪問陣列的能力,對於 ArrayAccess 這個類我前面文章有寫過,不瞭解的,可以去看看哦。
呼叫 static::$app[$name] 會直接去呼叫容器類的 offsetGet的方法:

    public function offsetGet($key)
    {
        return $this->make($key);
    }

上述程式碼,其中 make 方法繼續去解析和返回我們需要的例項,這部分程式碼不在本文的討論範圍之內,關於容器技術部落格其他文章中有詳細的描述。

至此,我們就揭開了 Laravel 框架中門面模式的面紗了。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
yefy

相關文章