Laravel深入學習12 – 依賴倒置原則

laravel發表於2019-02-16

宣告:本文並非博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當然也不是原汁原味的翻譯,能保證90%的原汁性,另外因為是理解翻譯,肯定會有錯誤的地方,歡迎指正。

歡迎轉載,轉載請註明出處,謝謝!

依賴反轉原則

介紹

我們來到了SOLID設計原則的最終的目標遠景!它就是依賴反轉原則,它是指高階程式碼不能依賴低階程式碼。相應的,高階程式碼應該依賴一個抽象層,它是在高階程式碼和低階程式碼之間的“中間人”角色。另一方面,該原則指代抽象層不依賴具體實現,而是細節依賴抽象。如果這個讀起來很晦澀,別擔心。我們下面會對這兩個方面具體的闡述本原則。

依賴反轉原則 本原則是指高階程式碼不依賴低階程式碼,抽象不依賴具體細節。

實探

如果你已經讀過本書之前的章節,就應該對依賴反轉有一個很好的理解。我們通過下面例子來解釋:

class Authenticator {

    public function __construct(DatabaseConnection $db)
    {
        $this->db = $db;
    }

    public function findUser($id)
    {
        return $this->db->exec(`select * from users where id = ?`, array($id));
    }

    public function authenticate($credentials)
    {
        // Authenticate the user...
    }

}

可以猜到,Authenticator類是負責查詢並驗證使用者的。我們來檢驗下類的構造器。可以看到我們有個連結資料庫的例項DatabaseConnection。所以我們將驗證器和資料庫緊密的接合在一起了,這意味著使用者物件必須建立在關係型資料庫查詢之上。此外,我們的高階程式碼(Authenticator類)直接依賴了低階程式碼(DatabaseConnection類)。 首先,我們解釋下“高階”和“低階”程式碼。低階程式碼實現像這種磁碟檔案訪問,資料庫接入等。高階程式碼在低階程式碼之上實現邏輯功能的封裝,但不能將他們耦合進來。或者,高階程式碼依賴建立在低階程式碼之上的抽象層,如介面。不僅如此,低階程式碼_也_依賴於抽象層。我們來實現一個可以在Authenticator類中使用的介面:

interface UserProviderInterface {
    public function find($id);
    public function findByUsername($username);
}

然後,將介面的實現注入到Authenticator

class Authenticator {

    public function __construct(UserProviderInterface $users,
    HasherInterface $hash)
    {
        $this->hash = $hash;
        $this->users = $users;
    }

    public function findUser($id)
    {
        return $this->users->find($id);
    }

    public function authenticate($credentials)
    {
        $user = $this->users->findByUsername($credentials[`username`]);

        return $this->hash->make($credentials[`password`]) == $user->password;
    }

}

這些改變之後,我們的Authenticator現在依賴兩個高階抽象:UserProviderInterfaceHasherInterface。我們就能自由的將任何針對介面的實現注入到Authenticator中了。比如,如果我們使用者儲存在Reids中,可以實現針對UserProvider實現一個RedisUserProvider類。Authenticator現在不在直接依賴低階的儲存操作了。 此外,自從它實現介面本身後,我們的低階程式碼現在也是依賴高階的UserProviderInterface抽象:

class RedisUserProvider implements UserProviderInterface {

    public function __construct(RedisConnection $redis)
    {
        $this->redis = $redis;
    }

    public function find($id)
    {
        $this->redis->get(`users:`.$id);
    }

    public function findByUsername($username)
    {
        $id = $this->redis->get(`user:id:`.$username);

        return $this->find($id);
    }

}

反轉思想 很多開發人員在應用中使用_反轉_原則。代替這種高階直接耦合低階程式碼的“自上而下”的方式,本原則指高階、低階程式碼“同時”依賴一個高階抽象層。

在我們將Authenticator的依賴“倒置”前,他是無法在其他資料儲存系統中使用的。在改變儲存系統的情況下,必須對Authenticator進行修改,違背了開放封閉原則。我們已經知道,幾種原則之間是相互貫穿的。 在將Authenticator強制實現在儲存層之上的抽象層,我們可以根據UserProviderInterface介面約定切換成任意其他儲存系統,而無需對Authenticator本身進行修改。傳統的依賴痛過“倒置”就能事程式碼變得非常靈活,易於改變!

相關文章