結合laravel Facade看外觀模式怎麼用?
當獨立子系統的開發完成後,如果兩個系統是客戶-供應商關係,也就是說其中一個子系統(客戶)需要使用另一個子系統(供應商)提供的服務時,我們可以通過外觀模式來對客戶子系統隱藏呼叫供應商系統的複雜性,客戶端只需通過facade呼叫相應的服務而無需涉及供應商具體的服務呼叫方法。下面通過laravel中的facade來說明:
在laravel中,我們經常通過facade來實現全域性呼叫某個方法而不需要例項化一個物件。通過檢視原始碼可以知道,所有的Facade都是繼承自同一個抽象父類Illuminate\Support\Facades\Facade
。
當我們呼叫Auth::guard(‘customer’)來返回一個guard例項的時候,只需要在呼叫這個方法的所在指令碼寫上use Illuminate\Support\Facades\Auth;
Illuminate\Support\Facades\Auth
的具體定義的時候,我們就會發現,這種呼叫都是基於php的魔術方法——__callstatic()。
所有Facade中都包含了這個繼承自Facade抽象父類的方法
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);
}
}
從程式碼中可以看出,最關鍵的一句就是`$instance = static::getFacadeRoot();
,因為呼叫的方法和引數我們都能通過callstatic獲取,關鍵是我們如何獲取到一個能執行這個方法的正確的物件,讓我們繼續往下扒。
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
原來底層是你,resolveFacadeInstance(static::getFacadeAccessor())
不管,繼續扒。
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
static::getFacadeAccessor()
為什麼直接丟擲錯誤,搞錯了?不對,剛才好像在哪見過??沒錯,每個Facade都會對這個方法進行重寫,如果沒有重寫就會丟擲錯誤,例如在 Illuminate\Support\Facades\Auth
中的實現是:
protected static function getFacadeAccessor()
{
return 'auth';
}
好了,看來接下來這個才是最核心的resolveFacadeInstance($name)
,
protected static function resolveFacadeInstance($name)
{
//判斷傳入引數是否是物件,是則直接返回,我意淫laravel框架告訴我們在定義自己的facade的時候可以直接在getFacadeAccessor返回一個物件
if (is_object($name)) {
return $name;
}
//不是物件的話,判斷是否已經例項化過這個物件,是的話就把之前例項化後儲存的物件拿去使
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
//沒有?!那隻能返回新的物件並儲存到$resolvedInstance中方便下次用了
return static::$resolvedInstance[$name] = static::$app[$name];
}
ps :這裡是$app是laravel的ioc容器
通過上面的例子可以看出,laravel通過外觀模式對外提供auth服務,向客戶端隱藏了具體的操作,實際底層是呼叫了某個具體的執行者來提供該項服務,這樣客戶端就無需知道相關細節,就可以使用開箱即用的服務。