使人瘋狂的 SOLID 原則: 介面隔離原則 (Interface Segregation Principle)

YC
程式愛好者
Published in
4 min readSep 15, 2020

今天我們要說的是第四個原則:介面隔離原則 (ISP)

如果還不知道什麼是介面的話,可以先看物件導向中的介面與抽象類別是什麼 ?

定義:No client should be forced to depend on methods it does not use.
白話翻譯:模組與模組之間的依賴,不應有用不到的功能可以被對方呼叫。

介面隔離原則其實並不困難。就如定義所說,因為模組之間的依賴不應有用不到的功能,所以我們可以透過介面來進行分割,把模組分得更合符本身的角色,也讓使用介面的角色只能分別接觸到應有的功能。

我先舉一個簡單的例子來說明何為違反 ISP:

class Car {

public function void openEngineMode() { /*...*/ }

public function void repairWheel() { /*...*/ }
public function void startEngine() { /*...*/ }

public function void move() { /*...*/ }
}class Driver {
Car myCar = new Car();
myCar.startEngine();
myCar.move();
myCar.openEngineMode(); // 為什麼我什麼都不會就可以開啟工程模式呢?
}
class Mechanic {
Car clientCar = new Car();
clientCar.repairWheel();
clientCar.openEngineMode();
}

上例中,我們看到一個普通人就可以使用 Car 類別中的 openEngineMode() 功能,但這功能其實只是給工程師用的,普通人用很容易會把汽車用壞。這就是一個沒有把功能隔離好的例子。

Car 的部分功能給了不該用到這些功能的角色使用。

這時我們可以用 interface 來做優化,如下:

interface DailyUsage {
public function void startEngine();
public function void move();
}
interface RepairUsage {
public function void openEngineMode();
public function void repairWheel();
}
class Car implement DailyUsage, RepairUsage {

public function void openEngineMode() { /*...*/ }

public function void repairWheel() { /*...*/ }
public function void startEngine() { /*...*/ }

public function void move() { /*...*/ }
}class Driver {
DailyUsage myCar = new Car();
myCar.startEngine();
myCar.move();
}
class Mechanic {
RepairUsage clientCar = new Car();
clientCar.repairWheel();
clientCar.openEngineMode();
}

透過 interface 來為我們的 Car 類別的不同功能分類,然後不同的角色再分別引用不同的功能。Mechanic 的程式將依賴於 RepairUsage 和 openEngineMode() ,但不再依賴於 Car。那 Mechanic 也不用關心 DailyUsage 和 Driver 的修改。

那架構層面來看呢?

好比當程式是依賴於 Framework 而 Framework 又賴依於 Database 時,當 Database 更換,如從 MySQL 換到 MongoDB,程式將會直接無法使用。

所以多使用介面來進行解藕、把實作隱藏起來、保持抽象,有助我們程式的彈性。

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

--

--

YC
程式愛好者

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