宣告:本文並非博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當然也不是原汁原味的翻譯,能保證90%的原汁性,另外因為是理解翻譯,肯定會有錯誤的地方,歡迎指正。
歡迎轉載,轉載請註明出處,謝謝!
里氏替換原則
簡介
別擔心,里氏替換原則實際上比他的名字好理解。他是指任何在任何接受抽象化類的地方其實現也被接受。通俗的講,類中使用介面實現的地方,不需要修改程式碼對於任意的介面實現類都將能使用。
里氏替換原則
該原則表示,程式中對於例項化物件的子型別,不需要修改程式碼,可以直接進行替換。
實探
我們繼續拿OrderProcessor
舉例來闡述該原則,看下這個方法:
public function process(Order $order)
{
// Validate order...
$this->orders->logOrder($order);
}
在Order
驗證之後,我們使用OrderRepositoryInterface
介面實現類來記錄訂單日誌。我們假定,當訂單處理未成熟時,我們將所有的訂單以CSV格式記錄到系統中。我們的額OrderRepositoryInterface
介面實現類為CsvOrderRepository
。當業務繼續發展,我們想使用關係型資料庫記錄訂單。下面,我們看下一種可能的介面實現:
class DatabaseOrderRepository implements OrderRepositoryInterface {
protected $connection;
public function connect($username, $password)
{
$this->connection = new DatabaseConnection($username, $password);
}
public function logOrder(Order $order)
{
$this->connection->run(`insert into orders values (?, ?)`, array(
$order->id, $order->amount,
));
}
}
現在,讓我們檢驗下如何將不得不去使用此實現:
public function process(Order $order)
{
// Validate order...
if ($this->repository instanceof DatabaseOrderRepository)
{
$this->repository->connect(`root`, `password`);
}
$this->repository->logOrder($order);
}
注意,在訂單處理類中,我們強制檢測了OrderRepositoryInterface
是否為一個資料庫的實現方式。如果是,繼續資料庫的連線。在小型應用中還算是小問題,但是,如果在很多其他類中OrderRepositoryInterface
被使用到時怎麼辦?我們就只能在所有地方去新增這段“引導”程式碼。這樣的程式碼維護讓人頭痛,還會程式碼潛在的bug,如果有一個地方忘記修改,就瞎了。
上述例項已違背里氏替換原則。在不修改connect
方法的情況下我們無法注入介面的實現類。既然發現了問題,那就去修復他們吧。這裡是一個新的DatabaseOrderRepository
實現類:
class DatabaseOrderRepository implements OrderRepositoryInterface {
protected $connector;
public function __construct(DatabaseConnector $connector)
{
$this->connector = $connector;
}
public function connect()
{
return $this->connector->bootConnection();
}
public function logOrder(Order $order)
{
$connection = $this->connect();
$connection->run(`insert into orders values (?, ?)`, array(
$order->id, $order->amount,
));
}
}
現在在DatabaseOrderRepository
中實現了資料庫連線的管理,我們就可以從OrderProcessor
中移除那段“引導”程式碼了:
public function process(Order $order)
{
// Validate order...
$this->repository->logOrder($order);
}
如此改變,我們就可以在OrderProcessor
中隨意使用CsvOrderRepository
或者DatabaseOrderRepository
了。我們的程式碼遵循了里氏替換原則。很多建築學上的概念都被討論成一種“認知”。特別的,對於每一個類,都有其自己的“語境”,他周邊的程式碼在其依賴環境下幫助類來完成特定的工作。當你讓架構朝著健壯方向發展的時候,這種類的設計“認知”將是一種持久重要的主題。
小心漏洞
你也許注意到了本原則和上章中提到的迴避“抽象漏洞”類似。我們的資料庫獲取部分就是破壞里氏替換的點,在你以後的編碼中一定要對這種編碼留心!