ISP 的核心理念非常直觀:「不應強迫客戶端(Client)依賴它不使用的方法。」

如果一個介面過於龐大(Fat Interface),包含許多不相關功能,
那麼依賴該介面的類別就會被捲入無謂變更中。

一、經典場景:胖介面的問題#

Uncle Bob 舉了一個經典例子: 假設有個巨大類別 OPS,它擁有三個方法 op1, op2, op3
系統中有三個不同使用者(User):

  • User1 只呼叫 op1
  • User2 只呼叫 op2
  • User3 只呼叫 op3

Figure 10.1: The Interface Segregation Principle

違反 ISP 的後果#

如果我們讓 User1 直接依賴這個巨大 OPS 類別:

  1. 不必要的重新編譯:User2 請求修改 op2 時,OPS 的原始碼會改變。
    雖然這改變跟 User1 無關,但因User1 依賴 OPS,所以 User1 也須被重新編譯和部署
  2. 錯誤蔓延: 如果 op2 的修改導致 OPS 發生編譯錯誤或崩潰,
    User1 也會無法運作,即便它根本沒用到那個壞掉功能

二、解決方案:介面隔離#

解決方法是將操作隔離成特定介面:U1Ops, U2Ops, U3Ops

  • User1 只依賴 U1Ops 介面(裡面只有 op1
  • OPS 類別去實作這三個介面

這樣一來,當 op2 改變時,只有 U2OpsUser2 受影響,
User1 則完全絕緣,不需要重新編譯。

Figure 10.2: Segregated operations

三、語言特性與架構意義#

1. 語言差異#

ISP 對於 靜態型別語言(如 Java, C++) 尤為重要,
因為這些語言原始碼的依賴(Source Code Dependency)會導致強制的重新編譯與重新部署。

對於 動態型別語言(如 Python, Ruby),這問題較不嚴重(因為依賴是在執行期推斷的),
但 ISP 仍然是良好的架構習慣。

2. 架構層次的啟示#

ISP 不僅適用於類別設計,更適用於系統架構:

  • 不要依賴你不需要的: 如果系統依賴了一個龐大框架(Framework),
    但只用了其中一小部分,你卻被迫引入了該框架所有依賴(Database, Libraries 等)
  • 攜帶包袱的風險: 這些沒用到的「包袱」可能會帶來麻煩(如安全漏洞、效能問題)

Figure 10.3: A problematic architecture

依賴一個包含「你不需要的功能」的模組是有害的。
透過 ISP,我們將龐大介面切為更小、具體的介面,確保模組間的依賴關係精確且必要。