Laravel深入學習10 – 里氏替換原則

laravel發表於2019-02-16

宣告:本文並非博主原創,而是來自對《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了。我們的程式碼遵循了里氏替換原則。很多建築學上的概念都被討論成一種“認知”。特別的,對於每一個類,都有其自己的“語境”,他周邊的程式碼在其依賴環境下幫助類來完成特定的工作。當你讓架構朝著健壯方向發展的時候,這種類的設計“認知”將是一種持久重要的主題。

小心漏洞

你也許注意到了本原則和上章中提到的迴避“抽象漏洞”類似。我們的資料庫獲取部分就是破壞里氏替換的點,在你以後的編碼中一定要對這種編碼留心!

相關文章