聲明:本文並不是博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,固然也不是原汁原味的翻譯,能保證90%的原汁性,另外由於是理解翻譯,確定會有錯誤的地方,歡迎指正。數據庫
歡迎轉載,轉載請註明出處,謝謝!單元測試
在項目的整個生命週期中,絕大多數時間是在現有代碼上的維護,而不是成天都在寫新功能。你會意識到,這是一個讓人頭大的過程。任何對代碼的修改,都會帶來破壞原有設計,引入新bug的風險。理想狀態下,咱們應是像寫新代碼同樣,去快速簡單的修改老的邏輯。若是在設計中,能正確使用開放封閉原則,就能很好的規避這些問題。測試
開放封閉原則this
SOLID設計原則中的開放封閉原則是指代碼對擴展開放,對修改關閉。編碼
咱們以上章中的OrderProcessor
爲基礎,繼續來探究開放封閉原則。對於process
方法中的邏輯:翻譯
$recent = $this->orders->getRecentOrderCount($order->account); if ($recent > 0) { throw new Exception('Duplicate order likely.'); }
這段代碼很是好讀,使用依賴注入的方法也讓咱們易於測試。可是,若是針對驗證的業務規則發生改編了怎麼辦?添加了新規則又怎麼辦?事實上,隨着業務的發展,確定會出現_不少_新的規則!process
方法會很快的變得癰腫起來而難以維護。由於從開放封閉原則的角度考慮,他對修改是開放的,因此每當需求變動咱們都要對代碼進行改變。牢記,咱們指望的是對_擴展_開放,而不是對修改開放。設計
代替掉在process
中直接使用訂單驗證的方式,咱們定義一個新接口OrderValidator
:code
interface OrderValidatorInterface { public function validate(Order $order); }
接下來,建立對重複訂單驗證的接口實現:接口
class RecentOrderValidator implements OrderValidatorInterface { public function __construct(OrderRepository $orders) { $this->orders = $orders; } public function validate(Order $order) { $recent = $this->orders->getRecentOrderCount($order->account); if ($recent > 0) { throw new Exception('Duplicate order likely.'); } } }
很好!如今一個小而可測的單獨的業務規則封裝類就完成了。咱們來在建立一個檢測用戶是否被停用的接口實現:生命週期
class SuspendedAccountValidator implememts OrderValidatorInterface { public function validate(Order $order) { if ($order->account->isSuspended()) { throw new Exception("Suspended accounts may not order.") } } }
如今咱們有了兩個對接口OrderValidatorInterface
的實現類,來看看怎麼在OrderValidatorInterface
中使用他們。咱們只需在訂單處理類實例化時注入驗證類,這樣就能在原有的訂單處理代碼基礎上輕鬆的添加或刪除驗證規則。
class OrderProcessor { public function __construct(BillerInterface $biller, OrderRepository $orders, array $validators = array()) { $this->biller = $biller; $this->orders = $orders; $this->validators = $validators; } }
接下來,只須要在process
方法中接入驗證便可:
public function process(Order $order) { foreach ($this->validators as $validator) { $validator->validate($order); } // Process valid order... }
最後,咱們需要將OrderProcessor
綁定到應用程序IoC容器中:
App::bind('OrderProcessor', function() { return new OrderProcessor( App::make('BillerInterface'), App::make('OrderRepository'), array( App::make('RecentOrderValidator'), App::make('SuspendedAccountValidator'), ), ); });
這些改變,只是在原有代碼上產生最小的影響,咱們如今就能不改變原來代碼的基礎上隨便添加或者刪除新的驗證規則。
每一個新的驗證規則只是對OrderValidatorInterface
的實現,並注入到容器中。不用對原來那種龐大臃腫的process
方法進行單元測試,如今只需單獨的對新驗證規則測試便可。如今代碼就是對擴展_開放_,對_修改_關閉。
玉有瑕疵
要注意依賴關係的實現細節。當依賴中的實現細節改變時,用戶邏輯是不該該更隨着改變的。當這種狀況發生時,咱們認爲實現細節在以來關係中是「有漏洞的」。當抽象邏輯有漏洞,那麼開閉原則也要被打破了。
在進一步處理以前,要知道開閉原則並不是硬規定。不是代碼的全部地方都得支持「熱插拔」。例如,那種規模很小,只是簡單的獲取幾行MySQL數據庫數據的邏輯並不需要你嚴格按照那些設計原則去編寫代碼。不要只是爲了用而在編碼中硬賽上這些設計原則,這反而讓你的系統過分設計,變得臃腫。不少設計原則是爲了解決大而複雜系統時提出的經常使用解決方案。可是,別拿這些話做爲你偷懶的藉口。