設計原則之【介面隔離原則】

Gopher大威發表於2022-02-24

和表妹去逛超市...

表妹:哥啊,我想買酸奶?

我:買,問題不大。

表妹:他家的酸奶都在搞活動,買酸奶送雞蛋。

我:那不挺好嘛?把雞蛋也一起買啦。

表妹:可是我家還有好多雞蛋......


你看,這種“捆綁式消費”,不就類似違反了我們軟體設計中的“介面隔離原則”嘛?雞蛋並不是我們想要的,但是它卻跟我們想要的酸奶綁在一起,酸奶就變得“臃腫”了。

客戶端不應該強迫依賴它不需要的介面。其中的“客戶端”,可以理解為介面的呼叫者或使用者。

如何理解“介面”?

理解“介面隔離原則”的重點是理解其中的“介面”二字。這裡有三種不同的理解:

  • 如果把“介面”理解為一組介面集合,可以是某個微服務的介面,也可以是類庫的介面等。如果部分介面只被部分呼叫者使用,我們就需要將這部分介面隔離出來,單獨給這部分呼叫者使用,而不強迫其他呼叫者也依賴這部分不會被用到的介面。

  • 如果把“介面”理解為單個API介面或函式,部分呼叫者只需要函式中的部分功能,那我們就需要把函式拆分成粒度更細的多個函式,讓呼叫者只依賴它需要的那個細粒度函式。

  • 如果把“介面”理解為OOP中的介面,也可以理解為物件導向程式語言中的介面語法。那介面的設計要儘量單一,不要讓介面的實現類和呼叫者,依賴不需要的介面函式。

那麼,接下來,我們從物件導向程式語言中的介面語法的角度來學習一下“介面隔離原則”。

我們一起來看“美女”,一般來說,長得好看的,身材不錯的,氣質出眾的都可以成為美女。

所以,我們定義了一個IPettyGirl介面,顯示美女的這幾個特徵。PettyGirl是該介面的實現類。

 1 // 美女介面
 2 public interface IPettyGirl {
 3     // 要有姣好的面孔
 4     public void goodLooking();
 5     // 要有好身材
 6     public void niceFigure();
 7     // 要有氣質
 8     public void greatTemperament();
 9 }
10 11 // 介面的實現類
12 public class PettyGirl implements IPettyGirl {
13     private String name;
14 15     public PettyGirl(String  name){
16        this.name = name;
17     }
18 19     // 臉蛋漂亮
20     public void goodLooking() {
21          System.out.println(this.name + "---臉蛋很漂亮!");
22     }
23 24     // 氣質要好
25     public void greatTemperament() {
26           System.out.println(this.name + "---氣質非常好!");
27     }
28 29     // 身材要好
30     public void niceFigure() {
31           System.out.println(this.name + "---身材非常棒!");
32     }
33 }

我們仔細想一下,長得好看,擁有好的身材,而且氣質還好的,當然是“美女”,甚至還是“女神”, “神仙小姐姐”,因為簡直是無可挑剔。這是所有人心目中的評判標準。

但是,我們每個人都會有自己不同的審美標準。假如,隔壁老王就認為“美女”應該是具有好看的皮囊,長得好看,身材好。老李就喜歡有趣的靈魂,認為氣質好的才是“美女”。

那麼,這樣看來,IPettyGirl介面的設計就顯得比較臃腫了,因為太寬泛了,其實每個人的審美標準都不同。所以,我們將該介面進行拆分,如下:

 1 // IGoodBodyGirl 好看的皮囊
 2 public interface IGoodBodyGirl {
 3      // 要有姣好的面孔
 4      public void goodLooking();
 5      // 要有好身材
 6      public void niceFigure();
 7 }
 8  9 // IGreatTemperamentGirl 有趣的靈魂
10 public interface IGreatTemperamentGirl {
11      // 要有氣質
12      public void greatTemperament();
13 }
14 15 // PettyGirl1 老王心目中的美女---長得好看,身材好
16 public class PettyGirl1 implements IGoodBodyGirl {
17      private String name;
18 19      public PettyGirl1(String _name){
20         this.name = _name;
21   }
22 23      // 臉蛋漂亮
24      public void goodLooking() {
25         System.out.println(this.name + "---臉蛋很漂亮!");
26      }
27 28      // 身材要好
29      public void niceFigure() {
30         System.out.println(this.name + "---身材非常棒!");
31      }
32 }
33 34 // PettyGirl2 老李心目中的美女---氣質好
35 public class PettyGirl2 implements IGreatTemperamentGirl {
36      private String name;
37      public PettyGirl2(String _name) {
38         this.name = _name;
39      }
40     
41      // 氣質要好
42      public void greatTemperament() {
43         System.out.println(this.name + "---氣質非常好!");
44      }
45 }

你看,這樣的設計,是不是更加靈活,易擴充套件了?

假如,有些人認為心靈美的人,才叫“美女”。我們只需擴充套件如下程式碼,而無需改動之前的程式碼。

 1 // IMindBeautyGirl 心靈美
 2 public interface IMindBeautyGirl {
 3     // 心靈美
 4     public void mindbeauty();
 5 }
 6  7 // PettyGirl3 心靈美的美女
 8 public class PettyGirl3 implements IMindBeautyGirl {
 9     private String name;
10     public PettyGirl3(String _name) {
11         this.name = _name;
12     }
13     
14     // 心靈美
15     public void mindbeauty() {
16         System.out.println(this.name + "---心靈美!");
17     }
18 }

但是,如果按照IPettyGirl這個介面設計的話,我們不但要改動IPettyGirl這個介面,還要改每一個實現類。改不好,可能還會引入新的bug。

你看,介面粒度小,設計的改動也比較少。

介面是我們設計時對外提供的契約,通過分散定義多個介面,可以預防未來變更的擴散,提供系統的靈活性和可維護性。

不過,你可能發現,介面隔離原則跟單一職責原則有點類似。但是,還是有點區別的。

介面隔離原則與單一職責原則的區別

單一職責原則針對的是模組、類、介面的設計,注重的是職責,這是業務邏輯上的劃分。

介面隔離原則相對於單一職責原則,一方面更側重於介面的設計,另一方面,它的思考角度也是不同的。

介面隔離原則提供了一種判斷介面的職責是否單一的標準:通過呼叫者如何使用介面來間接地判定。如果呼叫者只使用部分介面或介面的部分功能,那麼介面的設計就比較“臃腫”,違背了“介面隔離原則”。

那可能有同學會問:“如果一個介面,按照介面隔離原則要拆,但是按照單一職責原則就不用拆,那應該怎麼辦呢?”

其實很好辦,根據介面隔離原則拆分時,首先必須滿足單一職責原則。

總結

一個介面只幹一件事,介面要精簡單一。

目的:高內聚、低耦合。

好啦,每個設計原則是否運用得當,需要根據具體的業務場景,具體分析。

參考

極客時間專欄《設計模式之美》

《設計模式之禪》

 

相關文章