上一篇php
We have reached our final destination in our overview of the five SOLID design principles! The final principle is the Dependency Inversion principle, and it states that high-level code should not depend on low-level code. Instead, high-level code should depend on an abstraction layer that serves as a "middle-man" between the high and low-level code. A second aspect to the principle is that abstractions should not depend upon details, but rather details should depend upon abstractions. If this all sounds extremely confused, don't worry. We'll cover both aspects of this principle below.redis
在整個「堅實」原則概述的旅途中,咱們到達最後一站了!最後的原則是依賴反轉原則,它規定高等級的代碼不該該依賴(遷就)低等級的代碼。首先,高等級的代碼應該依賴(聽從)着抽象層,抽象層就像是「中間人」同樣,負責鏈接着高等級和低等級的代碼。其次,抽象定義不該該依賴(遷就)着具體實現,但具體實現應該依賴(聽從)着抽象定義。若是這些東西讓你極端困惑,別擔憂。接下來咱們會將這兩方面通通介紹給你。數據庫
Dependency Inversion Principle 依賴反轉原則
This principle states that high-level code should not depend on low-level code, and that abstractions should not depend upon details.app
該原則要求高等級代碼不該該遷就低等級代碼,抽象定義不該該遷就具體實現。ide
If you have already read prior chapters of this book, you already have a good grasp of the Dependency Inversion principle! To illustrate the principle, let's consider the following class:函數
若是你已經讀過了本書前面幾個章節,你就已經很好掌握了依賴反轉原則!爲了說明本原則,讓咱們考慮下面這個類:flex
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... } }
As you might have guessed, the Authenticator
class is responsible for finding and authenticating users. Let's examine the constructor of this class. You will see that we are type-hinting a DatabaseConnection
instance. So, we're tightly coupling our authenticator to the database, and essentially saying that users will always only be provided out of a relational SQL database. Furthermore, our high-level code (the Authenticator
) is directly depending on low-level code (the DatabaseConnection
).this
你可能猜到了,Authenticator
就是用來查找和驗證用戶的。繼續研究它的構造函數。咱們發現它使用了類型提示,要求傳入一個DatabaseConnection
對象,因此該驗證類和數據庫被緊密的聯繫在一塊兒。並且基本上講,這個數據庫還只能是關係數據庫。從而可知,咱們的高級代碼(Authenticator
)直接的依賴着低級代碼(DatabaseConnection
)。.net
First, let's discuss "high-level" and "low-level" code. Low-level code implements basic operations like reading files from a disk, or interaction with a database. High-level code encapsulates complex logic and relies on the low-level code to function, but should not be directly coupled to it. Instead, the high-level code should depend on an abstraction that sits on top of the low-level code, such as an interface. Not only that, but the low-level code should also depend upon an abstraction. So, let's write an interface that we can use within our Authenticator
:設計
首先咱們來談談「高級代碼」和「低級代碼」。低級代碼用於實現基本的操做,好比從磁盤讀文件,操做數據庫等。高級代碼用於封裝複雜的邏輯,它們依靠低級代碼來達到功能目的,但不能直接和低級代碼耦合在一塊兒。取而代之的是高級代碼應該依賴着低級代碼的頂層抽象,好比接口。不只如此,低級代碼也應當依賴着抽象。 因此咱們來寫個Authenticator
能夠用的接口:
interface UserProviderInterface { public function find($id); public function findByUsername($username); }
Next let's inject an implementation of this interface into our Authenticator
:
接下來咱們將該接口注入到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; } }
After making these changes, our Authenticator
now relies on two high-level abstractions: UserProviderInterface
and HasherInterface
. We are free to inject any implementation of these interfaces into the Authenticator
. For example, if our users are now stored in Redis, we can write a RedisUserProvider
which implements the UserProviderInterface
contract. The Authenticator
is no longer depending directly on low-level storage operations.
作了這些小改動後,Authenticator
如今依賴於兩個高級抽象:UserProviderInterface
和HasherInterface
。咱們能夠向Authenticator
自由的注入這倆接口的任何實現類。好比,若是咱們的用戶存儲在Redis裏面,咱們只需寫一個RedisUserProvider
來實現UserProviderInterface
接口便可。Authenticator
再也不依賴着具體的低級別的存儲操做了。
Furthermore, our low-level code now depends on the high-level UserProviderInterface
abstraction, since it implements the interface itself:
此外,因爲咱們的低級別代碼實現了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); } }
Inverted Thinking 反轉的思惟
Applying this principle inverts the way many developers design applications. Instead of coupling high-level code directly to low-level code in a "top-down" fashion, this principle states that both high and low-level code depend upon a high-level abstraction.
貫徹這一原則會反轉好多開發者設計應用的方式。再也不將高級代碼直接和低級代碼以「自上而下」的方式耦合在一塊兒,這個原則提出不管高級仍是低級代碼都要依賴於一個高層次的抽象。
Before we "inverted" the dependencies of our Authenticator
, it could not be used with anything other than a database storage system. If we changed storage system, our Authenticator
would also have to be modified, in violation of the Open Closed principle. Again, as we have seen before, multiple principles usually stand or fall together.
在咱們沒有反轉Authenticator
的依賴以前,它除了使用數據庫存儲系統別無選擇。若是咱們改變了存儲系統,Authenticator
也須要被修改,這就違背了開放封閉原則。咱們又一次看到,這些設計原則一般一榮俱榮一損俱損。
After forcing the Authenticator
to depend upon an abstraction over the storage layer, we can use it with any storage system that implements our UserProviderInterface
contract without any modifications to the Authenticator
itself. The conventional dependency chain has been inverted and our code is much more flexible and welcoming of change!
經過強制讓Authenticator
依賴着一個存儲抽象層,咱們就能夠使用任何實現了UserProviderInterface
接口的存儲系統,且不用對Authenticator
自己作任何修改。傳統的依賴關係鏈已經被反轉了,代碼變得更靈活,更加無懼變化!
完