定義:
客戶端不應該依賴它不需要的接口;類之間的依賴關系應建立在最小的接口之上。接口隔離原則英文全稱為Interface Segregation Principle ,簡稱為ISP。
個人理解:
通俗的來說,接口不能臃腫龐大,而使根據具體需要盡量的細化。接口中的方法也要盡可能的少。接口是設計對外的一種契約,通過分散定義多個接口可以預防將來變更的擴散,使得真個系統變得更加穩定和更具有可維護性。
問題由來:
類A通過接口I依賴類B,類C通過接口I依賴類D,如果接口I對於類A和類C不是最小的接口,那麼類C和類D中肯定存在對應接口I中不需要的方法。如下圖所示:
/// <summary> /// 接口I,定義了5個方法 /// </summary> public interface I { void Method1(); void Method2(); void Method3(); void Method4(); void Method5(); } /// <summary> /// 類A /// </summary> public class A { public void Depend1(I i) { i.Method1(); } public void Depend2(I i) { i.Method2(); } public void Depend3(I i) { i.Method3(); } } public class B : I { public void Method1() { Console.WriteLine("類B實現接口I的方法1"); } public void Method2() { Console.WriteLine("類B實現接口I的方法2"); } public void Method3() { Console.WriteLine("類B實現接口I的方法3"); } //對於類B來說,Method4和Method5方法不是必需的。但是既然繼承了接口I, //即使方法體為空,也要實現這兩個方法 public void Method4() { } public void Method5() { } } /// <summary> /// 類C /// </summary> public class C { public void Depend1(I i) { i.Method1(); } public void Depend2(I i) { i.Method4(); } public void Depend3(I i) { i.Method5(); } } public class D : I { public void Method1() { Console.WriteLine("類D實現接口I的方法1"); } //對於類B來說,Method4和Method5方法不是必需的。但是既然繼承了接口I, //即使方法體為空,也要實現這兩個方法 public void Method2() { } public void Method3() { } public void Method4() { Console.WriteLine("類D實現接口I的方法4"); } public void Method5() { Console.WriteLine("類D實現接口I的方法5"); } }
解決方案:
將臃腫的接口I細分為若干個接口,類A和類C分別與它們對應的接口建立依賴關系。接口隔離原則的核心定義是接口盡量小,不出現臃腫的接口(Fat Interface),但是“小”是有限度的,首先就是不能違反單一職責原則。如果一個接口設計過於臃腫,那麼繼承此接口的實例不管需不需要接口的所有方法都必須要實現該接口的所有方法。所以對於上面的解決是將接口I細分為3個接口,然後類A和C分別對應實現,類圖結構如下所示:
/// <summary> /// 接口I1,定義了方法1 /// </summary> public interface I1 { void Method1(); } /// <summary> /// 接口I2,定義了方法2和3 /// </summary> public interface I2 { void Method2(); void Method3(); } /// <summary> /// 接口I3,定義了方法4和5 /// </summary> public interface I3 { void Method4(); void Method5(); } /// <summary> /// 類A /// </summary> public class A { public void Depend1(I1 i) { i.Method1(); } public void Depend2(I2 i) { i.Method2(); } public void Depend3(I2 i) { i.Method3(); } } public class B : I1,I2 { public void Method1() { Console.WriteLine("類B實現接口I的方法1"); } public void Method2() { Console.WriteLine("類B實現接口I的方法2"); } public void Method3() { Console.WriteLine("類B實現接口I的方法3"); } } /// <summary> /// 類C /// </summary> public class C { public void Depend1(I1 i) { i.Method1(); } public void Depend2(I3 i) { i.Method4(); } public void Depend3(I3 i) { i.Method5(); } } public class D : I1,I3 { public void Method1() { Console.WriteLine("類D實現接口I的方法1"); } public void Method4() { Console.WriteLine("類D實現接口I的方法4"); } public void Method5() { Console.WriteLine("類D實現接口I的方法5"); } }
代碼結構如下:
看到該圖你就會很明白了吧!
接口隔離原則的含義是:建立單一接口,不要建立龐大臃腫的接口,盡量細化接口,接口中的方法盡量少。也就是說,我們要為各個類建立專用的接口,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調用。本文例子中,將一個龐大的接口變更為3個專用的接口所采用的就是接口隔離原則。在程序設計中,依賴幾個專用的接口要比依賴一個綜合的接口更靈活。接口是設計時對外部設定的“契約”,通過分散定義多個接口,可以預防外來變更的擴散,提高系統的靈活性和可維護性。
順便說一下單一職責原則和接口隔離原則的區別。單一職責原則針對的是類,其次才是接口和方法,它關注的關鍵點是“職責”,對應的是具體的實現部分要“單一”;而接口隔離原則針對的是抽象級別,它關注的是抽象與具體實現的隔離,對應的是抽象接口之間的隔離。
在實際使用接口隔離原則中我們應注意以下3點:
1.接口應盡量小,方法盡量少,這符合接口隔離原則定義,但是在具體項目中要適度,不可能創建非常多的接口,所以應用接口隔離原則也要具體問題具體分析。
2.定制服務,定制服務就是單獨為一個個體提供優良服務。
3.提高內聚(高內聚就是要提高接口、類、模塊的處理能力,減少對外的交互),減少對外交互,使用最少的方法完成盡可能多的事情。
接口隔離原則表明客戶端不應該被強迫實現一些他們不會使用的接口,應該把胖接口中的方法分組,然後用多個接口代替它,每個接口服務於一個子模塊。
接口隔離原則
不應該強迫客戶端依賴於他們不會使用的接口。
實例
下面是一個違反了接口隔離原則的例子。我們使用Manager類代表一個管理工人的管理者。有兩種類型的工人:普通的和高效的,這兩種工人都需要吃午飯。現在來了一批機器人,它們同樣為公司工作,但是他們不需要吃午飯。一方面Robot類需要實現IWoker接口,因為他們要工作,另一方面,它們又不需要實現IWorker接口,因為它們不需要吃飯。
在這種情況下IWorker就被認為是一個被污染了的接口。
如果我們保持現在的設計,那麼Robot類將被迫實現eat()方法,我們可以寫一個啞類它什麼也不做(比如說它只用一秒鐘的時間吃午飯),但是這會對程序造成不可預料的結果(例如管理者看到的報表中顯示被帶走的午餐多於實際的人數)。
根據接口隔離原則,一個靈活的設計不應該包含被污染的接口。對於我們的例子來說,我們應該把IWorker分離成2個接口。
3. interface IWorker {
4. public void work();
5.
6. public void eat();
7. }
8.
9. class Worker implements IWorker {
10. public void work() {
11. // ....working
12. }
13.
14. public void eat() {
15. // ...... eating in launch break
16. }
17. }
18.
19. class SuperWorker implements IWorker{
20. public void work() {
21. //.... working much more
22. }
23.
24. public void eat() {
25. //.... eating in launch break
26. }
27. }
28.
29. class Manager {
30. IWorker worker;
31.
32. public void setWorker(IWorker w) {
33. worker=w;
34. }
35.
36. public void manage() {
37. worker.work();
38. }
39. }
// interface segregation principle - bad ......余下全文>>
就是有些端口不用但是你要是不隔離可能會影響別的端口 一般式高電阻 或是接地