摘要:的契約是一組定義框架提供的核心服務(wù)的接口,例如我們在介紹用戶認證的章節(jié)中到的用戶看守器契約和用戶提供器契約以及框架自帶的模型所實現(xiàn)的契約。接口與團隊開發(fā)當你的團隊在開發(fā)大型應(yīng)用時,不同的部分有著不同的開發(fā)速度。
Contracts
Laravel 的契約是一組定義框架提供的核心服務(wù)的接口, 例如我們在介紹用戶認證的章節(jié)中到的用戶看守器契約IllumninateContractsAuthGuard 和用戶提供器契約IlluminateContractsAuthUserProvider
以及框架自帶的AppUser模型所實現(xiàn)的IlluminateContractsAuthAuthenticatable契約。
為什么使用契約通過上面幾個契約的源碼文件我們可以看到,Laravel提供的契約是為核心模塊定義的一組interface。Laravel為每個契約都提供了相應(yīng)的實現(xiàn)類,下表列出了Laravel為上面提到的三個契約提供的實現(xiàn)類。
契約 | Laravel內(nèi)核提供的實現(xiàn)類 |
---|---|
IllumninateContractsAuthGuard | IlluminateAuthSessionGuard |
IlluminateContractsAuthUserProvider | IlluminateAuthEloquentUserProvider |
IlluminateContractsAuthAuthenticatable | IlluminateFoundationAuthAuthenticatable(User Model的父類) |
所以在自己開發(fā)的項目中,如果Laravel提供的用戶認證系統(tǒng)無法滿足需求,你可以根據(jù)需求定義看守器和用戶提供器的實現(xiàn)類,比如我之前做的項目就是用戶認證依賴于公司的員工管理系統(tǒng)的API,所以我就自己寫了看守器和用戶提供器契約的實現(xiàn)類,讓Laravel通過自定義的Guard和UserProvider來完成用戶認證。自定義用戶認證的方法在介紹用戶認證的章節(jié)中我們介紹過,讀者可以去翻閱那塊的文章。
所以Laravel為所有的核心功能都定義契約接口的目的就是為了讓開發(fā)者能夠根據(jù)自己項目的需要自己定義實現(xiàn)類,而對于這些接口的消費者(比如:Controller、或者內(nèi)核提供的 AuthManager這些)他們不需要關(guān)心接口提供的方法具體是怎么實現(xiàn)的, 只關(guān)心接口的方法能提供什么功能然后去使用這些功能就可以了,我們可以根據(jù)需求在必要的時候為接口更換實現(xiàn)類,而消費端不用進行任何改動。
定義和使用契約上面我們提到的都是Laravel內(nèi)核提供的契約, 在開發(fā)大型項目的時候我們也可以自己在項目中定義契約和實現(xiàn)類,你有可能會覺得自帶的Controller、Model兩層就已經(jīng)足夠你編寫代碼了,憑空多出來契約和實現(xiàn)類會讓開發(fā)變得繁瑣。我們先從一個簡單的例子出發(fā),考慮下面的代碼有什么問題:
class OrderController extends Controller { public function getUserOrders() { $orders= Order::where("user_id", "=", Auth::user()->id)->get(); return View::make("order.index", compact("orders")); } }
這段代碼很簡單,但我們要想測試這段代碼的話就一定會和實際的數(shù)據(jù)庫發(fā)生聯(lián)系。也就是說, ORM和這個控制器有著緊耦合。如果不使用Eloquent ORM,不連接到實際數(shù)據(jù)庫,我們就沒辦法運行或者測試這段代碼。這段代碼同時也違背了“關(guān)注分離”這個軟件設(shè)計原則。簡單講:這個控制器知道的太多了。 控制器不需要去了解數(shù)據(jù)是從哪兒來的,只要知道如何訪問就行。控制器也不需要知道這數(shù)據(jù)是從MySQL或哪兒來的,只需要知道這數(shù)據(jù)目前是可用的。
Separation Of Concerns 關(guān)注分離Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class.
每個類都應(yīng)該只有單一的職責,并且職責里所有的東西都應(yīng)該由這個類封裝
接下來我們定義一個接口,然后實現(xiàn)該接口
interface OrderRepositoryInterface { public function userOrders(User $user); } class OrderRepository implements OrderRepositoryInterface { public function userOrders(User $user) { Order::where("user_id", "=", $user->id)->get(); } }
將接口的實現(xiàn)綁定到Laravel的服務(wù)容器中
App::singleton("OrderRepositoryInterface", "OrderRespository");
然后我們將該接口的實現(xiàn)注入我們的控制器
class UserController extends Controller { public function __construct(OrderRepositoryInterface $orderRepository) { $this->orders = $orderRespository; } public function getUserOrders() { $orders = $this->orders->userOrders(); return View::make("order.index", compact("orders")); } }
現(xiàn)在我們的控制器就完全和數(shù)據(jù)層面無關(guān)了。在這里我們的數(shù)據(jù)可能來自MySQL,MongoDB或者Redis。我們的控制器不知道也不需要知道他們的區(qū)別。這樣我們就可以獨立于數(shù)據(jù)層來測試Web層了,將來切換存儲實現(xiàn)也會很容易。
接口與團隊開發(fā)當你的團隊在開發(fā)大型應(yīng)用時,不同的部分有著不同的開發(fā)速度。比如一個開發(fā)人員在開發(fā)數(shù)據(jù)層,另一個開發(fā)人員在做控制器層。寫控制器的開發(fā)者想測試他的控制器,不過數(shù)據(jù)層開發(fā)較慢沒法同步測試。那如果兩個開發(fā)者能先以interface的方式達成協(xié)議,后臺開發(fā)的各種類都遵循這種協(xié)議。一旦建立了約定,就算約定還沒實現(xiàn),開發(fā)者也可以為這接口寫個“假”實現(xiàn)
class DummyOrderRepository implements OrderRepositoryInterface { public function userOrders(User $user) { return collect(["Order 1", "Order 2", "Order 3"]); } }
一旦假實現(xiàn)寫好了,就可以被綁定到IoC容器里
App::singleton("OrderRepositoryInterface", "DummyOrderRepository");
然后這個應(yīng)用的視圖就可以用假數(shù)據(jù)填充了。接下來一旦后臺開發(fā)者寫完了真正的實現(xiàn)代碼,比如叫RedisOrderRepository。那么使用IoC容器切換接口實現(xiàn),應(yīng)用就可以輕易地切換到真正的實現(xiàn)上,整個應(yīng)用就會使用從Redis讀出來的數(shù)據(jù)了。
接口與測試建立好接口約定后也更有利于我們在測試時進行Mock
public function testIndexActionBindsUsersFromRepository() { // Arrange... $repository = Mockery::mock("OrderRepositoryInterface"); $repository->shouldReceive("userOrders")->once()->andReturn(["order1", "order2]); App::instance("OrderRepositoryInterface", $repository); // Act... $response = $this->action("GET", "OrderController@getUserOrders"); // Assert... $this->assertResponseOk(); $this->assertViewHas("order", ["order1", "order2"]); }總結(jié)
接口在程序設(shè)計階段非常有用,在設(shè)計階段與團隊討論完成功能需要制定哪些接口,然后設(shè)計出每個接口具體要實現(xiàn)的方法,方法的入?yún)⒑头祷刂颠@些,每個人就可以按照接口的約定來開發(fā)自己的模塊,遇到還沒實現(xiàn)的接口完全可以先定義接口的假實現(xiàn)等到真正的實現(xiàn)開發(fā)完成后再進行切換,這樣既降低了軟件程序結(jié)構(gòu)中上層對下層的耦合也能保證各部分的開發(fā)進度不會過度依賴其他部分的完成情況。
本文已經(jīng)收錄在系列文章Laravel源碼學習里,歡迎訪問閱讀。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/29458.html
摘要:看起來很麻煩是吧,完全可以封裝一個函數(shù),直接調(diào)用就可以了,沒有必要自定義服務(wù)提供者創(chuàng)建契約。將自定義的服務(wù)提供者做如下修改修改了契約的綁定,所有使用契約進行依賴注入的實例,都會由實例切換到實例。 導語 laravel 的服務(wù)提供者,是框架的核心,提供了路由、日志、緩存等功能。這里要實現(xiàn)的需求是使用第三方 API 獲取天氣情況,涉及到服務(wù)提供者、契約、依賴注入等方面。相關(guān)內(nèi)容可以通過下方...
摘要:過去一年時間寫了多篇文章來探討了我認為的框架最核心部分的設(shè)計思路代碼實現(xiàn)。為了大家閱讀方便,我把這些源碼學習的文章匯總到這里。數(shù)據(jù)庫算法和數(shù)據(jù)結(jié)構(gòu)這些都是編程的內(nèi)功,只有內(nèi)功深厚了才能解決遇到的復雜問題。 過去一年時間寫了20多篇文章來探討了我認為的Larave框架最核心部分的設(shè)計思路、代碼實現(xiàn)。通過更新文章自己在軟件設(shè)計、文字表達方面都有所提高,在剛開始決定寫Laravel源碼分析地...
摘要:的核心概念包括服務(wù)容器服務(wù)提供者門面契約。所有服務(wù)提供者都需要繼承類。可以為服務(wù)提供者的方法設(shè)置類型提示。方法將在所有其他服務(wù)提供者均已注冊之后調(diào)用。同樣會整理成思維導圖的形式以方便記憶與回顧。 showImg(https://segmentfault.com/img/remote/1460000010771201); Laravel 的核心概念包括:服務(wù)容器、服務(wù)提供者、門面(Fac...
摘要:前言年底了不太忙,最近一段時間也一直在研究,就想寫篇關(guān)于比較深一點的教程系列啥的,于是就找到站長給開了寫教程的渠道。優(yōu)點的就是為藝術(shù)家創(chuàng)造的框架,它也是工程化的趨勢。項目維護方便也是事實。如果有遇到問題可以直接在教程下面留言。 前言 年底了不太忙,最近一段時間也一直在研究laravel,就想寫篇關(guān)于laravel比較深一點的教程系列啥的,于是就找到站長給開了寫教程的渠道。由于第一次寫,...
摘要:外觀模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。將使用者與子系統(tǒng)從直接耦合,轉(zhuǎn)變成由外觀類提供統(tǒng)一的接口給使用者使用,以降低客戶端與子系統(tǒng)之間的耦合度。接下來將深入分析外觀服務(wù)的加載過程。引導程序?qū)⒃谔幚碚埱笫峭瓿梢龑印? 本文首發(fā)于 深入淺出 Laravel 的 Facade 外觀系統(tǒng),轉(zhuǎn)載請注明出處。 今天我們將學習 Laravel 核心架構(gòu)中的另一個主題「Fac...
閱讀 3259·2021-09-22 15:58
閱讀 1724·2019-08-30 14:17
閱讀 1729·2019-08-28 18:05
閱讀 1514·2019-08-26 13:33
閱讀 692·2019-08-26 12:20
閱讀 616·2019-08-26 12:18
閱讀 3198·2019-08-26 11:59
閱讀 1412·2019-08-26 10:36