Laravel 控制反轉和門面模式概念詳解

閆肅的部落格發表於2015-03-03

這兩個概念對於 Laravel 的使用者來說應該並不陌生,尤其是當你希望擴充套件或者替換 Laravel 核心庫的時候,理解和合理使用它們可以極大提升 Laravel 的戰鬥力。這裡以建立一個自己的 ServiceProvider 為例理解 Inversion of Control 和 Facade 在 Laravel 中的應用。

控制反轉(Inversion of Control)

什麼是 IoC

控制反轉(Inversion of Control,縮寫為IoC),是物件導向程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查詢”(Dependency Lookup)。通過控制反轉,物件在被建立的時候,由一個調控系統內所有物件的外界實體,將其所依賴的物件的引用傳遞給它。 — 維基百科

簡單說來,就是一個類把自己的的控制權交給另外一個物件,類間的依賴由這個物件去解決。依賴注入屬於依賴的顯示申明,而依賴查詢則是通過查詢來解決依賴。

Laravel 中的使用

注入一個類:

App::bind('foo', function($app)
{
    return new FooBar;
});

這個例子的意思是建立一個別名為 foo 的類,使用時實際例項化的是 FooBar

使用這個類的方法是:

$value = App::make('foo');

$value 實際上是 FooBar 物件。

如果希望使用單例模式來例項化類,那麼使用:

App::singleton('foo', function()
{
    return new FooBar;
});

這樣的話每次例項化後的都是同一個物件。

注入類的更多例子可以看 Laravel 官網

你可能會疑問上面的程式碼應該寫在哪兒呢?答案是你希望他們在哪兒執行就寫在哪兒。0 —— 0 知道寫哪兒還用來看這種基礎文章麼!

服務提供器 (Service Providers)

為了讓依賴注入的程式碼不至於寫亂,Laravel 搞了一個 服務提供器(Service Provider)的東西,它將這些依賴聚集在了一塊,統一申明和管理,讓依賴變得更加容易維護。

Laravel 中的使用

定義一個服務提供器:

use Illuminate\Support\ServiceProvider;

class FooServiceProvider extends ServiceProvider {

    public function register()
    {
        $this->app->bind('foo', function()
        {
            return new Foo;
        });
    }

}

這個程式碼也不難理解,就是申明一個服務提供器,這個服務提供器有一個 register的方法。這個方法實現了我們上面講到的依賴注入。

當我們執行下面程式碼:

App::register('FooServiceProvider');

我們就完成一個注入了。但是這個還是得手動寫,所以怎麼讓 Laravel 自己來做這事兒呢?

我們只要在 app/config/app.php 中的 providers 陣列裡面增加一行:

'providers' => [
    …
       ‘FooServiceProvider’,
],

這樣我們就可以使用 App::make(‘foo’) 來例項化一個類了。

你不禁要問了,這麼寫也太難看了吧?莫慌,有辦法。

門面模式(Facade)

為了讓 Laravel 中的核心類使用起來更加方便,Laravel實現了門面模式。

外觀模式(Facade pattern),是軟體工程中常用的一種軟體設計模式,它為子系統中的一組介面提供一個統一的高層介面,使得子系統更容易使用。 — 維基百科

Laravel 中的使用

我們使用的大部分核心類都是基於門面模式實現的。例如:

$value = Cache::get('key');

這些靜態呼叫實際上呼叫的並不是靜態方法,而是通過 PHP 的魔術方法__callStatic() 講請求轉到了相應的方法上。

那麼如何講我們前面寫的服務提供器也這樣使用呢?方法很簡單,只要這麼寫:

use Illuminate\Support\Facades\Facade;

class Foo extends Facade {

    protected static function getFacadeAccessor() { return ‘foo’; }

}

這樣我們就可以通過 Foo::test() 來呼叫我們之前真正的 FooBar 類的方法了。

別名(Alias)

有時候我們可能將 Facade 放在我們擴充套件庫中,它有比較深的名稱空間,如:\Library\MyClass\Foo。這樣導致使用起來並不方便。Laravel 可以用別名來替換掉這麼長的名字。

我們只要在 app/config/app.php 中 aliases 下增加一行即可:

'aliases' => [
    …
    'Foo' => ‘Library\MyClass\Foo’,
],

這樣它的使用就由 \Library\MyClass\Foo::test() 變成 Foo::test() 了。

總結

所以有了控制反轉(Inversion of Control)和門面模式(Facade),實際還有服務提供器(Service Providers)和別名(Alias),我們建立自己的類庫和擴充套件 Laravel 都會方便很多。

這裡總結一下建立自己類庫的方法:

  1. 在 app/library/MyFoo 下建立類 MyFoo.php
  2. 在 app/library/MyFoo/providers 下建立 MyFooServiceProvider.php
  3. 在 app/library/MyFoo/facades 下建立 MyFooFacade.php
  4. 在 app/config/app.php 中新增 providers 和 aliases

相關文章