此為 SOLID 原則介紹的系列文章之一,所有文章的連結如下。
來到 SOLID 原則中最後一個原則,也就是依賴倒轉原則 (Dependency inversion principle)。
DIS 最主要的目的在於解耦 (Decoupling),其概念如下。
高層次的類別不應該依賴於低層次的類別,兩者都應該依賴於抽象的介面。
抽象介面不應該依賴於具體的實作,具體的實作應該依賴於抽象介面。
什麼是高層次與低層次的類別呢?
有時候在開發新系統的時候,我們可能會先設計低層次的類別,然後才會開始開發高層次的類別。
這樣開發流程十分常見,因為在低層次類別的功能還沒有確定與實作之前,我們也無法確定高層次類別可以實現哪些功能。
但這樣的開發流程,容易讓用來實現業務邏輯的高層次類別依賴於低層次的類別。
依賴倒轉原則建議改變這種依賴方式。
BudgetReport
。依賴倒轉原則通常會伴隨著開放封閉原則,無須修改已存在的類別,就能擴展不同的業務邏輯。
舉個例子,高層次的預算報告類別 BudgetReport
,使用低層次的資料庫類別 MySqlDatabase
來讀取和儲存資料,這代表低層次類別的任何改變 (例如資料庫發布新版本時),都會影響高層次的類別,但高層次的類別不應該去關注資料存儲的細節。
class MySqlDatabase {
insert() {
// ...
}
update() {
// ...
}
delete() {
// ...
}
}
class BudgetReport {
private mySqlDatabase: MySqlDatabase
constructor(mySqlDatabase: MySqlDatabase) {
this.mySqlDatabase = mySqlDatabase
}
open(date: string) {
//...
}
save() {
}
}
可以看到上述的程式碼,高層次的類別 BudgetReport
依賴於低層次的類別 MySqlDatabase
。
要解決這個問題,我們可以建立一個描述讀寫操作的高層次介面 Database
,並讓預算報告類別 BudgetReport
使用這個介面,而不是使用低層次的類別 MySqlDatabase
。
interface Database {
insert(): void
update(): void
delete(): void
}
class MySql implements Database {
insert(): void {
// ...
}
update(): void {
// ...
}
delete(): void {
// ...
}
}
class MongoDB implements Database {
insert(): void {
// ...
}
update(): void {
// ...
}
delete(): void {
// ...
}
}
class BudgetReport {
private database: Database
constructor(database: Database) {
this.database = database
}
open(date: string) {
//...
}
save() {
}
}
修改之後,低層次的類別依賴於高層次的抽象,原始的依賴關係被倒轉。
Laracasts 的講師是這麼說明依賴倒轉原則的。
Depends on abstractions, not on concretions.
依賴於抽象,而非具體。
後端工程師, PHP 基金會每月 5 鎂小額贊助人 稍微擅長 PHP、Python 與 Google Search,偶爾寫寫 TypeScript 對於逗號後面必須加空格有著絕對的堅持