完整的PHP依賴倒置原則例程

張京發表於2019-02-16

設計模式中依賴倒置原則(Dependence Inversion Principle)的定義是“高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。”理解起來並不難,但在具體實現上,網上給出的很多PHP示例都有缺陷。

就拿這篇文章來說,概念講的沒有問題,但在具體實現上,特別是程式碼中有很多錯誤,不能體現PHP特色,比如PHP中應該用->而不是用.來呼叫方法,變數名應該帶$等很多錯誤,這就不說了,最關鍵的是即使把這些語法錯誤都改正,例子也不能說明原則,不夠有說服力。因為即使不加介面或抽象類,媽媽也一樣能給孩子講故事、讀報紙、讀雜誌。

以下可執行程式碼,沒有用到任何介面和抽象類,一樣可以實現功能,並且可擴充套件,不需要修改Mother類裡的任何程式碼,一樣可以輕鬆自如地讓媽媽讀各種讀物,無非就是在上面追加各種class,只要這個class裡有getContent方法,媽媽全部可以識別:

<?php
class Book {
    public function getContent(){
        return "很久很久以前有一個阿拉伯的故事……
";
    }
}

class Newspaper {
    public function getContent(){
        return "林書豪17+9助尼克斯擊敗老鷹……
";
    }
}

class Mother{
    public function narrate($book){
        echo "媽媽開始講故事
";
        echo $book->getContent();
    }
}

class Client{
    public static function main(){
        $mother = new Mother();
        $mother->narrate(new Book());
        $mother->narrate(new Newspaper());
    }
}

Client::main();

既然如此隨意,還如何體現依賴倒置呢?這是因為PHP弱型別語言,特點就是不需要為變數指定型別,導致的結果就是隻要你的class裡有我需要呼叫的方法(在這裡是getContent方法),那就無論如何也不會出錯,至於你是不是實現了什麼interface介面,都無所謂的。像這樣,是無法真正體現依賴倒置原則的。那到底如何才能真正體現依賴倒置呢?祕訣就是我們通過使用PHP的型別約束來規定narrate函式的$book引數必須是一個介面:

class Mother{
    public function narrate(IReader $book){
        echo "媽媽開始講故事
";
        echo $book->getContent();
    }
}

在這裡,我們規定了$book引數必須是一個IReader介面,那麼凡是需要讓媽媽講的讀物都必須是對於IReader這個介面的一個實現,否則就會報錯。完整程式碼如下:

<?php
interface IReader{
    public function getContent();
}

class Book implements IReader {
    public function getContent(){
        return "很久很久以前有一個阿拉伯的故事……
";
    }
}

class Newspaper implements IReader {
    public function getContent(){
        return "林書豪17+9助尼克斯擊敗老鷹……
";
    }
}

class Mother{
    public function narrate(IReader $book){
        echo "媽媽開始講故事
";
        echo $book->getContent();
    }
}

class Client{
    public static function main(){
        $mother = new Mother();
        $mother->narrate(new Book());
        $mother->narrate(new Newspaper());
    }
}

$client = new Client();
$client->main();

你可以試著把class Newspaper後面的implements IReader去掉然後執行一下,馬上就會報錯:

PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Mother::narrate() must implement interface IReader, instance of Newspaper given, called in /Users/zhangjing/Projects/phpdesignpattern/client.php on line 29 and defined in /Users/zhangjing/Projects/phpdesignpattern/client.php:19

所以結論是:對於PHP這種弱型別語言來講,要想真正實現依賴倒置原則,必須加上型別約束,否則實現的只是表象,並不能真正體現原則的作用。

相關文章