宣告:本文並非博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當然也不是原汁原味的翻譯,能保證90%的原汁性,另外因為是理解翻譯,肯定會有錯誤的地方,歡迎指正。
歡迎轉載,轉載請註明出處,謝謝!
開放封閉原則
簡介
在專案的整個生命週期中,絕大多數時間是在現有程式碼上的維護,而不是整天都在寫新功能。你會意識到,這是一個讓人頭大的過程。任何對程式碼的修改,都會帶來破壞原有設計,引入新bug的風險。理想狀態下,我們應是像寫新程式碼一樣,去快速簡單的修改老的邏輯。如果在設計中,能正確使用開放封閉原則,就能很好的規避這些問題。
開放封閉原則
SOLID設計原則中的開放封閉原則是指程式碼對擴充套件開放,對修改關閉。
實探
我們以上章中的OrderProcessor
為基礎,繼續來探究開放封閉原則。對於process
方法中的邏輯:
$recent = $this->orders->getRecentOrderCount($order->account);
if ($recent > 0)
{
throw new Exception(`Duplicate order likely.`);
}
這段程式碼非常好讀,使用依賴注入的方法也讓我們易於測試。但是,如果針對驗證的業務規則發生改編了怎麼辦?新增了新規則又怎麼辦?事實上,隨著業務的發展,肯定會出現_很多_新的規則!process
方法會很快的變得癰腫起來而難以維護。因為從開放封閉原則的角度考慮,他對修改是開放的,所以每當需求變更我們都要對程式碼進行改變。牢記,我們期望的是對_擴充套件_開放,而不是對修改開放。
代替掉在process
中直接使用訂單驗證的方式,我們定義一個新介面OrderValidator
:
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資料庫資料的邏輯並不須要你嚴格按照那些設計原則去編寫程式碼。不要只是為了用而在編碼中硬賽上這些設計原則,這反而讓你的系統過度設計,變得臃腫。很多設計原則是為了解決大而複雜系統時提出的常用解決方案。但是,別拿這些話作為你偷懶的藉口。