使人瘋狂的 SOLID 原則:依賴反向原則 (Dependency Inversion Principle)

YC
程式愛好者
Published in
5 min readSep 21, 2020

--

今天我們要說的是最後一個原則:依賴反向原則 (DIP)

定義:高層模組不應依賴低層模組,它們都應依賴於抽象介面。抽象介面不應該依賴於具體實作,具體實作應依賴抽象介面。

什麼是高層模組,什麼是低層模組?

低層模組:該模組的實現都是不可分割的原子邏輯層,如 MVC 中的 Model 層。

高層模組:該模組的業務邏輯多是由低層模組組合而成,如 MVC 中的 Controller 層與 Client 端。

「正常的」程式,可能會有很多高層模組依賴於底層模組。我們常常看到的可能是像以下:

class Controller {

Mysql mysqlDB = new Mysql();
string dbHost = mysqlDB.host;
}

我們都在某個 function 或是 某個 class 底下直接使用其他的 class。這樣的問題是變得非常高耦合,如果今天我們要把 Mysql 改成 monogoDB,那整段程式碼應該就是直接換掉重寫了,完全違反了 OCP

那如果寫成以下的方式:

class Controller {
private Mysql mysqlDB;
public Controller(Mysql mysqlDB)
{
this.mysqlDB = mysqlDB;
}
string dbHost = this.mysqlDB.host;}

這樣寫是可以算是進了一步,我們使用了依賴注入來把 Mysql 放到 Controller 中,而不是直接在 Controller 中 new 出一個新的 object 出來,可以算是解耦了一點點

那到底怎麼樣寫才是乎合 DIP 原則的寫法多用抽象層!

class Controller {
private Database database;
public Controller(Database database) //只注入 Database 介面
{
this.database = database;
}
string dbHost = this.database.gethost();
}
interface Database {
public string getHost();
public string getPort();
public string getUsername();
public string getPassword();
}
class Mysql implement Database {
public string getHost() {
...
return host;
}
...
}

以上的例子,我們可以看到在 Controller 當中只依賴於抽象層 (interface Database),而 Mysql 類別也同樣賴於相同的抽象層。今天就算是要換成 MonogoDB,那只要一樣用 MonogoDB 類別賴於 Database 介面,Controller不用做出改變就可以加以擴充。

這樣的做法其實是一種將低層模組的控制權從原來的高層模組中抽離,將兩者的耦合只放在抽象層上

物件的建立則應以抽象工廠模式進行,同樣的原因,就是避免高度耦合的發生。我們來看看以下例子:

class Application {
...
DatabaseFactory db = new DatabaseFactoryImpl();
Database database = db.makeDb();
}
interface DatabaseFactory {
public Database makeDb();
}
class DatabaseFactoryImpl implment DatabaseFactory {
public Database makeDb() {
return new Mysql();
}
}
interface Database {
public string getHost();
public string getPort();
public string getUsername();
public string getPassword();
}
class Mysql implement Database {
public string getHost() {
...
return host;
}
...
}

以上的工廠能讓高層模組最低限度地依賴其他的模組 (只依賴於 DatabaseFactory 介面),而實現邏輯的會是於 DatabaseFactoryImpl 中發生。
同時,DatabaseFactory 產生的物件又能被 Database 介面所使用,讓 Application 這個類別完全不必接觸到非抽象層,將耦合性大大降低。

有些朋友會想到那在更大的範圍來看軟體時,那程式不是一樣依賴於 Framework 跟 Database 嗎?

對的,沒錯,程式的確是依賴於這樣不夠抽象的層次,但是

我們知道可以信任他們不會改變,所以我們可以容忍那些具體的依賴關係。

不是所有時候都需要 100% 按照 SOLID 原則來設計,這樣只會變得沒完沒了,總會得到一個無法再相依賴於抽象層的模組。

不要過度設計。

以上, 我理解的 SOLID原則終於告一段落,謝謝各位收看。

如果你覺得我的文章幫助到你,希望你也可以為文章拍手,分別 Follow 我的個人頁與程式愛好者出版,按讚我們的粉絲頁喔,支持我們推出更多更好的內容創作!

--

--

YC
程式愛好者

提供更精確的技術內容為目標,另創立「程式愛好者」專頁。資深軟體工程師,專研後端技術、物件導向、軟體架構。