摘要:然而,我們需要注意的是僅是軟件設(shè)計模式依賴注入的一種便利的實現(xiàn)形式。容器本身不是依賴注入的必要條件,在框架他只是讓其變得更加簡便。首先,讓我們探索下為什么依賴注入是有益的。繼續(xù)深入讓我們通過另一個示例來加深對依賴注入的理解。
聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證90%的原汁性,另外因為是理解翻譯,肯定會有錯誤的地方,歡迎指正。
歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處,謝謝!
依賴注入 問題所在Laravel框架的基礎(chǔ)在于其IoC容器。要想真正了解框架的核心,需要對容器有一定的概念。然而,我們需要注意的是IoC僅是軟件設(shè)計模式:依賴注入的一種便利的實現(xiàn)形式。容器本身不是依賴注入的必要條件,在框架他只是讓其變得更加簡便。
首先,讓我們探索下為什么依賴注入是有益的。考慮到如下代碼中的類和方法:
class UserController extends BaseController { public function getIndex() { $users = User::all(); return View::make("users.index", compact("users")); } }
代碼簡潔易懂,但是在沒有連接到數(shù)據(jù)庫的情況下,我們是無法進(jìn)行測試的。換句話說, Eloquent ORM 被緊密耦合到控制器中了。在未連接數(shù)據(jù)庫的情況下,我們無法測試當(dāng)前引用了Eloquent ORM的控制器的方法。這段代碼同樣違背了軟件設(shè)計原則 關(guān)注點分離(SoC) 。簡言之:控制器知道的太多。控制器無需知道數(shù)據(jù)_從何而來_,只需關(guān)注如何接入;無需關(guān)心數(shù)據(jù)庫在MySQL中是否可用,而只關(guān)心數(shù)據(jù)在_某處_可用。
關(guān)注點分離(Separation Of Concerns):
每個類都應(yīng)有其單一的職責(zé),并且這個職責(zé)由這個類完全封裝
所以,將web層(controller)從數(shù)據(jù)層解耦分離出來會是有益的。這在我們對數(shù)據(jù)進(jìn)行存儲遷移時是有利的,也會使代碼的測試更為簡單。將“Web”認(rèn)為是到“真正”應(yīng)用的傳輸層。
想象一下,應(yīng)用是一臺有著多種電纜接口的顯示器。我們能通過HDMI,VGA或者DVI接入顯示功能。也可將應(yīng)用比喻成你接入互聯(lián)網(wǎng)的電纜。顯示器的主要功能大部分依賴著電纜。而電纜僅僅是一種類似HTTP接入你應(yīng)用的傳輸部件。所以我們不想將這部分內(nèi)容(控制器)和應(yīng)用邏輯糅合在一塊。這種做法可以允許任何傳輸層,比如API或者移動應(yīng)用程序來接入我們的應(yīng)用邏輯。
所以,我們再次注入一個存儲類,來代替現(xiàn)有將控制器和Eloquent ORM糅合在一塊的做法。
契約式設(shè)計http://www.jdon.com/36303
首先,我們定義一個接口和相應(yīng)的實現(xiàn):
interface UserRepositoryInterface { public function all(); } class DbUserRepository implements UserRepositoryInterface { public function all() { return User::all()->toArray(); } }
接下來,我們向控制器中注入此接口的實現(xiàn)。
class UserController extends BaseController { public function __construct(UserRepositoryInterface $users) { $this->users = $users; } public function getIndex() { $users = $this->users->all(); return View::make("users.index", compact("users")); } }
現(xiàn)在,我們的控制器根本不曉得數(shù)據(jù)存儲在何處,無知是福啊!我們的數(shù)據(jù)可以來自MySQL,MongoDB,甚至是來自Redis。我們不知道這其中的區(qū)別,也不需要關(guān)心。僅僅這一點小小的改變,我們就可以將web層從數(shù)據(jù)層脫離,當(dāng)然當(dāng)數(shù)據(jù)存儲改變時也不會影響到我們。
服從邊限
記得服從職責(zé)限定。應(yīng)用中控制器和路由是HTTP和程序交互的中簡介,在大型程序中,不能將他們糅合到你的主要邏輯中。
為了鞏固上面的知識,我們從一個測試案例開始。首先,模擬一個庫并綁定到IoC容器中,然后確保控制器正確的調(diào)用了該庫:
public function testIndexActionBindsUsersFromRepository() { // Arrange... $repository = Mockery::mock("UserRepositoryInterface"); $repository->shouldReceive("all")->once()->andReturn(array("foo")); App::instance("UserRepositoryInterface", $repository); // Act... $response = $this->action("GET", "UserController@getIndex"); // Assert... $this->assertResponseOk(); $this->assertViewHas("users", array("foo")); }
繼續(xù)深入你在模仿我么
示例中,我們使用了Mockery模擬庫,它提供了一套表述簡潔的方法來模仿你的程序。Mockery可通過Composer進(jìn)行安裝。
讓我們通過另一個示例來加深對依賴注入的理解。有這樣一個場景,我們需要對用戶賬戶中發(fā)生的財務(wù)變更用進(jìn)行通知。這里我們定義兩個接口,或者叫約定。這些約定將會使需求變更變的很便捷。
interface BillerInterface { public function bill(array $user, $amount); } interface BillingNotifierInterface { public function notify(array $user, $amount); }
緊接著,我們來實現(xiàn)BillerInterface接口:
class StripeBiller implements BillerInterface { public function __construct(BillingNotifierInterface $notifier) { $this->notifier = $notifier; } public function bill(array $user, $amount) { // Bill the user via Stripe... $this->notifier->notify($user, $amount); } }
由于各個類之間已經(jīng)進(jìn)行了職責(zé)分離,為財務(wù)賬單(billing)類注入不同的通知程序?qū)芊奖恪1热缱⑷攵绦磐ㄖ?b>SmsNotifier或者郵件通知類EmailNotifier。我們的賬單系統(tǒng)不需要考慮賬單通知的實現(xiàn),只需要根據(jù)約定執(zhí)加載通知即可。凡是遵守約定的賬單,都能實現(xiàn)對用戶財務(wù)變更的通知。此外,不光我們添加方便,我們也可以多帶帶模擬BillingNotifierInterface接口,來測試賬單系統(tǒng)。
善用接口
接口寫起來看似添加了很多額外的東西,實際是在加速我們的開發(fā)。我們可以在不實現(xiàn)接口的情況下,模擬已開發(fā)的接口,來對整個底層邏輯進(jìn)行測試。
那問題來了,怎么實現(xiàn)依賴注入呢?
$biller = new StripeBiller(new SmsNotifier);
如上,簡單吧,這就是依賴注入。只需要將通知器傳入到賬單系統(tǒng),而不用擔(dān)心通知器的使用。微小的改動就能是代碼很清晰,這種清晰的職責(zé)界定設(shè)計,讓我們使代碼維護(hù)簡單,當(dāng)然也方便模擬測試。
那IoC容器是怎么一回事?依賴注入必須要用到他么?這里當(dāng)然不是!在以后的章節(jié)中,我們會看到IoC容器只是為了更好的組織管理依賴注入,但它并非必須。只要遵循本章中介紹的設(shè)計原則,你可以在任何項目中實現(xiàn)依賴注入,也不用管是否有這樣一個容器可用。
太多JAVA了吧很多人指責(zé),在PHP中使用接口把代碼變的太過冗長,太象“JAVA”。你必須定義一個接口并實現(xiàn)一個類,這得多敲多少代碼。
在小而簡的項目中,我承認(rèn)這種批判。這樣的項目中,接口是不必要的,因為就你自己用,以后也不會去改。即使架構(gòu)上牛逼的架構(gòu)師也會說“需求永遠(yuǎn)不會確定”,但是需要承認(rèn)的是,總有tm那么一些地方就是改不著。
接口在大型項目中是非常有用的,這樣額外的代碼是為了保證未來你代碼的靈活性和可測試性。當(dāng)你快速切換代碼實現(xiàn)的時候,一定會閃瞎某些人的狗眼。當(dāng)然我們的目的是為了讓代碼能夠適應(yīng)各種操蛋需求的變更。
總之,我們一直提倡“簡潔”架構(gòu)。如果如果你的項目很小,不需要遵循這么多規(guī)范,也別不好意思。代碼敲的怎么爽怎么來。如果不寫接口,也行,以后再說唄,又不是結(jié)婚買房,都tm逼的。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/22563.html
摘要:劃下重點,服務(wù)容器是用于管理類的依賴和執(zhí)行依賴注入的工具。類的實例化及其依賴的注入,完全由服務(wù)容器自動的去完成。 本文首發(fā)于 深入剖析 Laravel 服務(wù)容器,轉(zhuǎn)載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應(yīng)并最終呈現(xiàn)給用戶的工作原理。 本章將帶領(lǐng)大...
摘要:控制反轉(zhuǎn)容器控制反轉(zhuǎn)使依賴注入變得更加便捷。有瑕疵控制反轉(zhuǎn)容器是實現(xiàn)的控制翻轉(zhuǎn)容器的一種替代方案。容器的獨立使用即使沒有使用框架,我們?nèi)匀豢梢栽陧椖恐惺褂冒惭b組件來使用的控制反轉(zhuǎn)容器。在沒有給定任何信息的情況下,容器是無法實例化相關(guān)依賴的。 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味...
摘要:本文一大半內(nèi)容都是通過舉例來讓讀者去理解什么是控制反轉(zhuǎn)和依賴注入,通過理解這些概念,來更加深入。這種由外部負(fù)責(zé)其依賴需求的行為,我們可以稱其為控制反轉(zhuǎn)。工廠模式,依賴轉(zhuǎn)移當(dāng)然,實現(xiàn)控制反轉(zhuǎn)的方法有幾種。 容器,字面上理解就是裝東西的東西。常見的變量、對象屬性等都可以算是容器。一個容器能夠裝什么,全部取決于你對該容器的定義。當(dāng)然,有這樣一種容器,它存放的不是文本、數(shù)值,而是對象、對象的描...
摘要:在改變存儲系統(tǒng)的情況下,必須對進(jìn)行修改,違背了開放封閉原則。傳統(tǒng)的依賴痛過倒置就能事代碼變得非常靈活,易于改變 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證90%的原汁性,另外因為是理解翻譯,肯定會有錯誤的地方,歡迎指正。 歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處,謝謝! 依賴反轉(zhuǎn)原則 ...
摘要:如果在設(shè)計中,能正確使用開放封閉原則,就能很好的規(guī)避這些問題。開放封閉原則設(shè)計原則中的開放封閉原則是指代碼對擴(kuò)展開放,對修改關(guān)閉。實探我們以上章中的為基礎(chǔ),繼續(xù)來探究開放封閉原則。在進(jìn)一步處理之前,要知道開閉原則并非硬規(guī)定。 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證9...
閱讀 3190·2021-11-10 11:35
閱讀 1295·2019-08-30 13:20
閱讀 1117·2019-08-29 16:18
閱讀 2131·2019-08-26 13:54
閱讀 2155·2019-08-26 13:50
閱讀 955·2019-08-26 13:39
閱讀 2473·2019-08-26 12:08
閱讀 1951·2019-08-26 10:37