laravel依賴注入和控制反轉

依賴注入與控制反轉php

依賴注入 當我第一次接觸這個詞的時候,我是有些丈二和尚摸不着頭腦的,至今我也是感到比較困惑的,因此今天咱們來探索一下Laravel中的依賴注入(dependency injection), 來好好的理解它。 控制反轉 第一印象是好深奧的名詞。。。看上去好像是說反向控制?不懂?那就理順之!laravel

起點

什麼是依賴

沒有你我就活不下去,那麼,你就是個人依賴。 說白了就是:程序員

不是我自身的,倒是我須要的,都是我所依賴的。一切須要外部提供的,都是須要進行依賴注入的。編程

咱們用代碼來描述一下:app

class Boy {
  protected $girl;

  public function __construct(Girl $girl) {
    $this->girl = $girl;
  }
}

class Girl {
  ...
}

$boy = new Boy();  // Error; Boy must have girlfriend!

// so 必需要給他一個女友才行 
$girl = new Girl();

$boy = new Boy($girl); // Right! So Happy!

從上述代碼咱們能夠看到Boy強依賴Girl必須在構造時注入Girl的實例才行。函數

那麼爲何要有依賴注入這個概念,依賴注入到底解決了什麼問題?單元測試

咱們將上述代碼修正一下咱們初學時都寫過的代碼:測試

class Boy {
  protected $girl;

  public function __construct() {
    $this->girl = new Girl();
  }
}

這種方式與前面的方式有什麼不一樣呢?ui

咱們會發現Boy的女友被咱們硬編碼到Boy的身體裏去了。。。 每次Boy重生本身想換個類型的女友都要把本身扒光才行。。。 (⊙o⊙)…this

某天Boy特別喜歡一個LoliGirl ,很是想讓她作本身的女友。。。怎麼辦? 重生本身。。。扒開本身。。。把Girl扔了。。。把 LoliGirl塞進去。。。

class LoliGirl {

}

class Boy {
  protected $girl; 

  public function __construct() {
      //  $this->girl = new Girl();  // sorry...
      $this->girl = new LoliGirl();
  }
}

某天 Boy迷戀上了御姐.... (⊙o⊙)… Boy 好煩。。。

是否是感受不太好?每次遇到真心相待的人卻要這麼的折磨本身。。。

Boy說,我要變的強大一點。我不想被改來改去的!

好吧,咱們讓Boy強大一點:

interface Girl {
  // Boy need knows that I have some abilities.
}

class LoliGril implement Girl {
  // I will implement Girl's abilities.
}

class Vixen implement Girl {
  // Vixen definitely is a girl, do not doubt it.
}

class Boy {
  protected $girl;

  public function __construct(Girl $girl) {
    $this->girl = $girl;
  }
}

$loliGirl = new LoliGirl();
$vixen = new Vixen();

$boy = new Boy($loliGirl);
$boy = new Boy($vixen);

Boy很高興,終於能夠不用扒開本身就能夠體驗不一樣的人生了。。。So Happy!

小結

由於大多數應用程序都是由兩個或者更多的類經過彼此合做來實現業務邏輯,這使得每一個對象都須要獲取與其合做的對象(也就是它所依賴的對象)的引用。若是這個獲取過程要靠自身實現,那麼將致使代碼高度耦合而且難以維護和調試。

因此纔有了依賴注入的概念,依賴注入解決了如下問題:

  • 依賴之間的解耦
  • 單元測試,方便Mock

=。= 前面的依賴注入竟然須要咱們手動的去注入依賴,作爲程序員的咱們怎麼能夠容忍這種低效的注入方式,好吧,咱們先來了解一下IOC的概念.

控制反轉 (Inversion Of Control, IOC)

控制反轉 是面向對象編程中的一種設計原則,能夠用來減低計算機代碼之間的耦合度。其中最多見的方式叫作依賴注入(Dependency Injection, DI), 還有一種叫"依賴查找"(Dependency Lookup)。經過控制反轉,對象在被建立的時候,由一個調控系統內全部對象的外界實體,將其所依賴的對象的引用傳遞給它。也能夠說,依賴被注入到對象中。

也就是說,咱們須要一個調控系統,這個調控系統中咱們存放一些對象的實體,或者對象的描述,在對象建立的時候將對象所依賴的對象的引用傳遞過去。 在Laravel中Service Container就是這個高效的調控系統,它是laravel的核心。 下面咱們看一下laravel是如何實現自動依賴注入的。

laravel中的依賴注入

如今咱們看文檔給的例子應該就不難理解了:

<?php

namespace App\Jobs;

use App\User;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Bus\SelfHandling;

class PurchasePodcast implements SelfHandling
{
    /**
     * The mailer implementation.
     */
    protected $mailer;

    /**
     * Create a new instance.
     *
     * @param  Mailer  $mailer
     * @return void
     */
    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    /**
     * Purchase a podcast.
     *
     * @return void
     */
    public function handle()
    {
        //
    }
}

In this example, the PurchasePodcast job needs to send e-mails when a podcast is purchased. So, we willinject a service that is able to send e-mails. Since the service is injected, we are able to easily swap it out with another implementation. We are also able to easily "mock", or create a dummy implementation of the mailer when testing our application.

說到laravel中的依賴注入,咱們不得不瞭解laravel的Service Container

服務容器 (Service Container)

The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

從介紹不難看出服務容器就是控制反轉的容器,它就是前文說到的調度系統。實現依賴注入的方式能夠是在構造函數中或者setter方法中。

若是咱們仔細研究了Service Container咱們就會發現laravel的服務容器中只存儲了對象的描述,而並不須要知道如何具體的去構造一個對象,由於它會根據php的反射服務去自動解析具體化一個對象。

反射

在計算機科學中,反射是指計算機在運行時(Run time)能夠訪問、檢測和修改它自己狀態或行爲的一種能力。用來比喻說,那種程序可以「觀察」而且修改本身的行爲。

支持反射的語言提供了一些在低級語言中難以實現的運行時特性。這些特性包括

  • 做爲一個第一類對象發現並修改源代碼的結構(如代碼塊、類、方法、協議等)。
  • 將跟class或function匹配的轉換成class或function的調用或引用。
  • 在運行時像對待源代碼語句同樣計算字符串。
  • 建立一個新的語言字節碼解釋器來給編程結構一個新的意義或用途。

PHP實現的反射能夠在官網文檔中進行查看: 反射API

Example
$reflector = new ReflectionClass('App\User');

if ($reflector->isInstantiable()) {
  $user = $refector->newInstance(); //in other case you can send any arguments
}

laravel的服務容器的build方法中須要經過反射服務來解析依賴關係,好比說construct函數中須要傳遞的依賴參數有哪些? 它就須要用到以下方法:

$constructor = $reflector->getConstructor();

   // If there are no constructors, that means there are no dependencies then
   // we can just resolve the instances of the objects right away, without
   // resolving any other types or dependencies out of these containers.
   if (is_null($constructor)) {
       array_pop($this->buildStack);

       return new $concrete;
   }

   $dependencies = $constructor->getParameters();

如今咱們應該對laravel如何實現依賴的自動注入有點想法了吧?來整理一下疑問:

  • 如何實現依賴的自動注入? (控制反轉,利用反射)
  • 依賴注入須要哪些東東? (整理依賴關係[ construct | setter ],還要解析依賴傳遞引用)
  • 怎麼解析依賴?

你可能會問爲何要問怎麼解析依賴?解析依賴確定是要用到反射的啦,反射,你知道類名不就能夠直接解析了嗎?

其實。。。不是這樣的。。。(@ο@)

不少時候咱們爲了提升代碼的擴展性和維護性,在編寫類時依賴的是接口或抽象類,而並非一個具體的實現類。明白了嗎?依賴解析的時候若是隻解析到接口或抽象類,而後利用反射,那麼這個依賴確定是錯誤的。

那麼咱們就須要在調度系統中注入相關依賴的映射關係,而後在須要的時候正確的解析關係。 好比說, 喂, 我須要一個 A, 你別給我 B 啊。

$container->bind('a', function () {
  return new B();  // just this for you
});

$a = $container->make('a');

總結

  • 依賴注入是控制反轉的一種實現,實現代碼解耦,便於單元測試。由於它並不須要瞭解自身所依賴的類,而只須要知道所依賴的類實現了自身所須要的方法就能夠了。你須要我,你卻不認識我/(ㄒoㄒ)/~~
  • 控制反轉提供一種調控系統,實現依賴解析的自動注入,通常配合容器提供依賴對象實例的引用。
相關文章
相關標籤/搜索