和表妹去逛超市...
表妹:哥啊,我想買酸奶??
我:買,問題不大,
表妹:他家的酸奶都在搞活動,買酸奶送雞蛋,
我:那不挺好嘛?把雞蛋也一起買啦,
表妹:可是我家還有好多雞蛋......
你看,這種“捆綁式消費”,不就類似違反了我們軟體設計中的“介面隔離原則”嘛?雞蛋并不是我們想要的,但是它卻跟我們想要的酸奶綁在一起,酸奶就變得“臃腫”了,
客戶端不應該強迫依賴它不需要的介面,其中的“客戶端”,可以理解為介面的呼叫者或使用者,
如何理解“介面”?
理解“介面隔離原則”的重點是理解其中的“介面”二字,這里有三種不同的理解:
-
如果把“介面”理解為一組介面集合,可以是某個微服務的介面,也可以是類別庫的介面等,如果部分介面只被部分呼叫者使用,我們就需要將這部分介面隔離出來,單獨給這部分呼叫者使用,而不強迫其他呼叫者也依賴這部分不會被用到的介面,
-
如果把“介面”理解為單個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,
你看,介面粒度小,設計的改動也比較少,
介面是我們設計時對外提供的契約,通過分散定義多個介面,可以預防未來變更的擴散,提供系統的靈活性和可維護性,
不過,你可能發現,介面隔離原則跟單一職責原則有點類似,但是,還是有點區別的,
介面隔離原則與單一職責原則的區別
單一職責原則針對的是模塊、類、介面的設計,注重的是職責,這是業務邏輯上的劃分,
介面隔離原則相對于單一職責原則,一方面更側重于介面的設計,另一方面,它的思考角度也是不同的,
介面隔離原則提供了一種判斷介面的職責是否單一的標準:通過呼叫者如何使用介面來間接地判定,如果呼叫者只使用部分介面或介面的部分功能,那么介面的設計就比較“臃腫”,違背了“介面隔離原則”,
那可能有同學會問:“如果一個介面,按照介面隔離原則要拆,但是按照單一職責原則就不用拆,那應該怎么辦呢?”
其實很好辦,根據介面隔離原則拆分時,首先必須滿足單一職責原則,
總結
一個介面只干一件事,介面要精簡單一,
目的:高內聚、低耦合,
好啦,每個設計原則是否運用得當,需要根據具體的業務場景,具體分析,
參考
極客時間專欄《設計模式之美》
《設計模式之禪》
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/432046.html
標籤:其他
上一篇:設計原則之【介面隔離原則】
