摘要:別堵塞了傳輸層大多數(shù)事件處理器被當(dāng)作傳輸層組件。解耦事件處理器開始本命題前,我們來使用一個示例。假想下把隊列處理器用來發(fā)送消息給用戶。盡量避免在事件處理器中摻雜太多的業(yè)務(wù)邏輯。
聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證90%的原汁性,另外因為是理解翻譯,肯定會有錯誤的地方,歡迎指正。
歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處,謝謝!
應(yīng)用體系結(jié)構(gòu):解耦事件處理器 介紹現(xiàn)在我們已經(jīng)介紹了很多使用Laravel 4構(gòu)建健壯應(yīng)用的特性,下面來深入挖掘更多的細(xì)節(jié)。本章我們將討論諸如隊列、事件這些眾多事件處理器的解耦,也包括類似“類事件”結(jié)構(gòu)的路由過濾。
解耦事件處理器別堵塞了傳輸層
大多數(shù)“事件處理器”被當(dāng)作_傳輸層_組件。換言之,隊列處理、事件觸發(fā)器、或者一個外來請求都被用來調(diào)用某些調(diào)用處理。要像處理控制器一樣處理這些事件處理器,并避免在其中涉及太多業(yè)務(wù)邏輯。
開始本命題前,我們來使用一個示例。假想下把隊列處理器用來發(fā)送SMS消息給用戶。在發(fā)送消息之后,處理器講發(fā)送了的消息記錄成歷史以便我們知道有哪些用戶收到了這些消息。代碼實現(xiàn)如下:
class SendSMS{ public function fire($job, $data) { $twilio = new Twilio_SMS($apiKey); $twilio->sendTextMessage(array( "to"=> $data["user"]["phone_number"], "message"=> $data["message"], )); $user = User::find($data["user"]["id"]); $user->messages()->create(array( "to"=> $data["user"]["phone_number"], "message"=> $data["message"], )); $job->delete(); } }
僅測試這塊代碼,就可能遇到一些問題。首先,測試?yán)щy。Twilio_SMS類是在fire方法中實例化的,這意味著我們無法使用注入的方式模擬服務(wù)。其次,在處理器中我們直接用到了Eloquent模型,這就給測試帶來了另外一個問題,我們必須在方法中進(jìn)行真正的數(shù)據(jù)庫訪問。最后,我們在隊列之外無法進(jìn)行SMS消息發(fā)送。我們的SMS消息發(fā)送邏輯完全糅合在Laravel隊列中了。
通過將邏輯提取到某一“服務(wù)”中的方法,我們可以將應(yīng)用中的SMS消息發(fā)送邏輯從Laravel的隊列服務(wù)中解耦出來。從而可以在應(yīng)用中的任何地方發(fā)送消息。當(dāng)我們進(jìn)行了這種解耦處理,這種重構(gòu)也是我們的代碼變得更加具有可測性。
讓我們來修改下代碼:
class User extends Eloquent { /** * Send the User an SMS message * * @param SmsCourierInterface $courier * @param string $message * @return SmsMessage */ public function sendSmsMessage(SmsCourierInterface $courier, $message) { $courier->sendMessage($this->phone_number, $message); return $this->sms()->create(array( "to"=> $this->phone_number, "message"=> $message, )); } }
在這個重構(gòu)的代碼實例中,我們將發(fā)送消息的邏輯提取到User模型中。同時向該方法中注入SmsCourierInterface接口實現(xiàn)邏輯,使我們更好的測試邏輯中的方方面面。重構(gòu)了短信發(fā)送邏輯之后,再對隊列進(jìn)行重構(gòu):
class SendSMS { public function __construct(UserRepository $users, SmsCourierInterface $courier) { $this->users = $users; $this->courier = $courier; } public function fire($job, $data) { $user = $this->users->find($data["user"]["id"]); $user->sendSmsMessage($this->courier, $data["message"]); $job->delete(); } }
在重構(gòu)的示例中,可以看到,隊列服務(wù)已經(jīng)足夠輕量。它在隊列和我們_真正的_應(yīng)用邏輯之間已經(jīng)足夠符合_傳輸層_這個概念。贊!這意味著我們可以在隊列之外輕易的發(fā)送消息。最后,讓我們編寫一些測試代碼:
class SmsTest extends PHPUnit_Framework_TestCase { public function testUserCanBeSentSmsMessages() { /** * Arrage ... */ $user = Mockery::mock("User[sms]"); $relation = Mockery::mock("StdClass"); $courier = Mockery::mock("SmsCourierInterface"); $user->shouldReceive("sms")->once()->andReturn($relation); $relation->shouldReceive("create")->once()->with(array( "to" => "555-555-5555", "message" => "Test", )); $courier->shouldReceive("sendMessage")->once()->with( "555-555-5555", "Test" ); /** * Act ... */ $user->sms_number = "555-555-5555"; $user->sendMessage($courier, "Test"); } }其他事件處理器
我們可以改進(jìn)很多這種類型的“事件處理器”。將他們限定為簡單的“傳輸層”來使用,能將復(fù)雜的業(yè)務(wù)邏輯很好的組織和解耦到框架之外。為了鞏固下這種思想,下面我們舉例一個路由過濾器,用它來驗證用戶是否為我們的“高級”訂閱用戶。
Route::filter("premium", function() { return Auth::user() && Auth::user()->plan == "premium"; });
乍看像是沒什么問題。這么小的代碼能有啥問題呢?然而,在這么小的過濾中,也能意識到我們將應(yīng)用的實現(xiàn)細(xì)節(jié)暴漏了出來。注意,我們在過濾中進(jìn)行對plan屬性進(jìn)行了檢測。“級別”的檢測邏輯層緊緊的揉進(jìn)了路由、傳輸層。如果我們將“高級”訂閱用戶的套餐存放到數(shù)據(jù)庫或者用戶模型中,這里又必須對我們的路由過濾器進(jìn)行修改!
相應(yīng)的,做些小的改編:
Route::filter("premium", function() { return Auth::user() && Auth::user()->isPremium(); });
這樣小的改編帶來的效果是明顯的,付出的代價也是小的。通過在模型中對用戶是否屬于高級訂閱用戶的判斷,我們將路由中的檢測邏輯解耦了出來。我們的過濾程序不在負(fù)責(zé)檢測用戶訂閱級別的職責(zé)。相應(yīng)的,它只需簡單的詢問用戶模型即可。現(xiàn)在,如果訂閱級別的判斷存放在數(shù)據(jù)庫中,路由過濾不需要更改任何代碼!
該誰負(fù)責(zé)?
我們又一次討論了_職責(zé)_的概念。牢記,一個類應(yīng)有的職責(zé)是什么,和他涉及的范圍是明確的。盡量避免在事件處理器中摻雜太多的業(yè)務(wù)邏輯。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/23009.html
摘要:控制只是用來接收請求并請求邏輯處理類。事實上,業(yè)務(wù)邏輯無需感知網(wǎng)絡(luò),網(wǎng)絡(luò)僅僅接入應(yīng)用的傳輸機(jī)制,他不應(yīng)超出應(yīng)用中的路由和控制器的范疇。職責(zé)分離是編寫健壯應(yīng)用的關(guān)鍵。其他通常,類庫應(yīng)該以規(guī)范組織在我們的應(yīng)用中。 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證90%的原汁性,另...
摘要:事件驅(qū)動編程是圖形用戶界面和其他應(yīng)用程序例如應(yīng)用程序中使用的主要范例,用于執(zhí)行某些操作來響應(yīng)用戶輸入。我們來看一下事件驅(qū)動編程帶來的收益。現(xiàn)在讓我們看看采用事件驅(qū)動編程方法如何實現(xiàn)上述相同的功能。 在這篇文章中我們將了解到什么是事件驅(qū)動編程以及在Laravel中如何開始構(gòu)建一個事件驅(qū)動應(yīng)用,同時我們還將看到如何通過事件驅(qū)動編程來對應(yīng)用程序的邏輯進(jìn)行解耦。 在開始之前,先說明一下這篇文章...
摘要:劃下重點(diǎn),服務(wù)容器是用于管理類的依賴和執(zhí)行依賴注入的工具。類的實例化及其依賴的注入,完全由服務(wù)容器自動的去完成。 本文首發(fā)于 深入剖析 Laravel 服務(wù)容器,轉(zhuǎn)載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應(yīng)并最終呈現(xiàn)給用戶的工作原理。 本章將帶領(lǐng)大...
摘要:一個服務(wù)提供器必須包含至少一種方法。服務(wù)提供器一旦被注冊,就可被用于程序的各個地方。注意服務(wù)提供器的變量來自類中。啟動服務(wù)當(dāng)所有的服務(wù)提供器注冊之后,他們就變成了已啟動狀態(tài)。再次提示,把服務(wù)提供器作為一種組織工具來使用。 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證90%...
摘要:它是良好應(yīng)用設(shè)計的大原則,包含單一責(zé)任原則開放封閉原則里氏替換原則接口分離原則依賴倒置原則讓我們通過代碼示例來深究下這五個原則。實探單一責(zé)任原則代表一個類有且僅有一個改變的原因,換言之,一個類的職責(zé)范疇是嚴(yán)謹(jǐn)明確的。 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當(dāng)然也不是原汁原味的翻譯,能保證90%的原...
閱讀 1214·2021-09-26 09:55
閱讀 3159·2019-08-30 15:55
閱讀 949·2019-08-30 15:53
閱讀 2286·2019-08-30 13:59
閱讀 2367·2019-08-29 13:08
閱讀 1098·2019-08-29 12:19
閱讀 3290·2019-08-26 13:41
閱讀 411·2019-08-26 13:24