依賴注入與控制反轉
依賴注入 當我第一次接觸這個詞的時候谚赎,我是有些丈二和尚摸不著頭腦的,至今我也是感到比較困惑的祠够,所以今天我們來探索一下Laravel中的依賴注入(dependency injection)
來好好的理解它综芥。
控制反轉 第一印象是好深奧的名詞,看上去好像是說反向控制忘蟹?不懂泰演?那就理順之!
起點
什么是依賴
沒有你我就活不下去葱轩,那么睦焕,你就是我的依賴。
說白了就是:
不是我自身的靴拱,卻是我需要的垃喊,都是我所依賴的。一切需要外部提供的袜炕,都是需要進行依賴注入的本谜。
我們用代碼來描述一下:
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();
}
}
這種方式與前面的方式有什么不同呢?
我們會發(fā)現(xiàn)Boy
的女朋友被我們硬編碼到Boy
的身體里去了陌知。他托。。 每次Boy
重生自己想換個類型的女朋友都要把自己扒光才行仆葡。赏参。。 (⊙o⊙)…
某天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!
小結
因為大多數(shù)應用程序都是由兩個或者更多的類通過彼此合作來實現(xiàn)業(yè)務邏輯凤价,這使得每個對象都需要獲取與其合作的對象(也就是它所依賴的對象)的引用鸽斟。如果這個獲取過程要靠自身實現(xiàn),那么將導致代碼高度耦合并且難以維護和調(diào)試料仗。
所以才有了依賴注入的概念湾盗,依賴注入解決了以下問題:
依賴之間的解耦
單元測試,方便Mock
=立轧。= 前面的依賴注入居然需要我們手動的去注入依賴格粪,做為程序員的我們怎么可以容忍這種低效的注入方式躏吊,好吧,我們先來了解一下IOC的概念.
控制反轉 (Inversion Of Control, IOC)
控制反轉 是面向?qū)ο缶幊讨械囊环N設計原則比伏,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection, DI), 還有一種叫"依賴查找"(Dependency Lookup)疆导。通過控制反轉赁项,對象在被創(chuàng)建的時候,由一個調(diào)控系統(tǒng)內(nèi)所有對象的外界實體澈段,將其所依賴的對象的引用傳遞給它悠菜。也可以說,依賴被注入到對象中败富。
也就是說悔醋,我們需要一個調(diào)控系統(tǒng),這個調(diào)控系統(tǒng)中我們存放一些對象的實體兽叮,或者對象的描述芬骄,在對象創(chuàng)建的時候?qū)ο笏蕾嚨膶ο蟮囊脗鬟f過去。
在Laravel中Service Container
就是這個高效的調(diào)控系統(tǒng)鹦聪,它是laravel的核心账阻。
下面我們看一下laravel是如何實現(xiàn)自動依賴注入的。
laravel中的依賴注入
現(xiàn)在我們看文檔給的例子應該就不難理解了:
<?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 will inject 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.
從介紹不難看出服務容器就是控制反轉的容器淘太,它就是前文說到的調(diào)度系統(tǒng)。實現(xiàn)依賴注入的方式可以是在構造函數(shù)中或者setter
方法中规丽。
如果我們仔細研究了Service Container
我們就會發(fā)現(xiàn)laravel的服務容器中只存儲了對象的描述琴儿,而并不需要知道如何具體的去構造一個對象,因為它會根據(jù)php的反射服務
去自動解析具體化一個對象嘁捷。
反射
在計算機科學中,反射是指計算機在運行時(Run time)可以訪問显熏、檢測和修改它本身狀態(tài)或行為的一種能力雄嚣。用來比喻說,那種程序能夠“觀察”并且修改自己的行為喘蟆。
支持反射的語言提供了一些在低級語言中難以實現(xiàn)的運行時特性缓升。這些特性包括
- 作為一個第一類對象發(fā)現(xiàn)并修改源代碼的結構(如代碼塊、類蕴轨、方法港谊、協(xié)議等)。
- 將跟class或function匹配的轉換成class或function的調(diào)用或引用橙弱。
- 在運行時像對待源代碼語句一樣計算字符串歧寺。
- 創(chuàng)建一個新的語言字節(jié)碼解釋器來給編程結構一個新的意義或用途燥狰。
PHP實現(xiàn)的反射可以在官網(wǎng)文檔中進行查看: 反射API
Example
$reflector = new ReflectionClass('App\User');
if ($reflector->isInstantiable()) {
$user = $refector->newInstance(); //in other case you can send any arguments
}
laravel的服務容器的build
方法中需要通過反射服務
來解析依賴關系,比如說construct
函數(shù)中需要傳遞的依賴參數(shù)有哪些? 它就需要用到如下方法:
$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();
現(xiàn)在我們應該對laravel如何實現(xiàn)依賴的自動注入有點想法了吧斜筐?來整理一下疑問:
如何實現(xiàn)依賴的自動注入龙致? (控制反轉,利用反射)
依賴注入需要哪些東東顷链? (整理依賴關系[ construct | setter ]目代,還要解析依賴傳遞引用)
怎么解析依賴?
你可能會問為什么要問怎么解析依賴嗤练?解析依賴肯定是要用到反射的啦榛了,反射,你知道類名不就可以直接解析了嗎煞抬?
其實霜大。。此疹。不是這樣的僧诚。。蝗碎。(@ο@)
很多時候我們?yōu)榱颂岣叽a的擴展性和維護性湖笨,在編寫類時依賴的是接口或抽象類,而并不是一個具體的實現(xiàn)類蹦骑。明白了嗎慈省?依賴解析的時候如果只解析到接口或抽象類,然后利用反射眠菇,那么這個依賴肯定是錯誤的边败。
那么我們就需要在調(diào)度系統(tǒng)中注入相關依賴的映射關系,然后在需要的時候正確的解析關系捎废。
比如說笑窜, 喂, 我需要一個 A, 你別給我 B 啊登疗。
$container->bind('a', function () {
return new B(); // just this for you
});
$a = $container->make('a');
總結
依賴注入是控制反轉的一種實現(xiàn)排截,實現(xiàn)代碼解耦,便于單元測試辐益。因為它并不需要了解自身所依賴的類断傲,而只需要知道所依賴的類實現(xiàn)了自身所需要的方法就可以了。
你需要我智政,你卻不認識我/(ㄒoㄒ)/~~
控制反轉提供一種調(diào)控系統(tǒng)认罩,實現(xiàn)依賴解析的自動注入,一般配合容器提供依賴對象實例的引用续捂。
推薦閱讀: